摘要:聚合函數在中,有一些用來統計匯總的函數,被稱作聚合函數,如等。方法其它方法如之類的編寫就不一一展示了,代碼請看聚合函數。如何獲取總數當然是使用上面講到的聚合函數來處理。
where 相關的子句構造完成后,我們繼續構造其它子句。這一篇我們進行聚合函數、分組、排序等子句的構造。
聚合函數在 SQL 中,有一些用來統計、匯總的函數,被稱作聚合函數,如 SUM、COUNT、AVG 等。
使用 select() 方法時,我們可以用 select("COUNT(id)") 這種寫法來使用聚合函數,但是這種方式有缺點:
語義上并不直觀
在防止關鍵字沖突時需要手動添加引號(參見第三篇條件查詢)
拿到聚合數據需要一串繁瑣的方法調用
為了更方便的獲得聚合數據,我們需要為其多帶帶編寫方法。
getList() 方法獲得某一列的方法可以由 PDO::FETCH_COLUMN 來完成。
基類添加 getList() 方法:
public function getList($field) { $this->_cols_str = " ".self::_quote($field)." "; $this->_buildQuery(); $this->_execute(); // 獲取一列數據 return $this->_pdoSt->fetchAll(PDO::FETCH_COLUMN, 0); }count() 方法
基類添加 count() 方法:
public function count($field = "*") { // 判斷是否時 * // 非 * 時給字段添加引號 if(trim($field) != "*") { $field = self::_quote($field); } // 構造列查詢字符串 $this->_cols_str = " COUNT(".$field.") AS count_num "; // 取結果 return $this->row()["count_num"]; }
構造 SQL SELECT COUNT(id) FROM test_table;
$results = $driver->table("test_table") ->count("id");
由于聚合函數方法和 get()、row() 方法一樣是取結果的,鏈式調用時切記要放到最后執行。
其它方法sum()、avg() 等方法沒有 COUNT("*") 這樣的場景,比 count() 方法的編寫更簡單。
sum() 方法:
public function sum($field) { $this->_cols_str = " SUM(".self::_quote($field).") AS sum_num "; return $this->row()["sum_num"]; }
其它方法如 avg()、max() 之類的編寫就不一一展示了,代碼請看 WorkerF - 聚合函數 。
分組:group by 和 havinggroup by 子句指定了要分組的字段,可以是一個或多個 (用逗號隔開)。
having 子句和 group by 子句一起使用,作為分組的篩選條件,可以為空,除了關鍵字外,語法和 where 子句基本相同。
基類新增 groupBy() 方法:
public function groupBy($field) { // 是否初次調用 ? if($this->_groupby_str == "") { $this->_groupby_str = " GROUP BY ".self::_wrapRow($field); } else { // 非初次調用(多個分組字段),使用逗號分隔 $this->_groupby_str .= " , ".self::_wrapRow($field); } return $this; }
基類新增 having()、orHaving() 方法:
public function having() { $operator = "AND"; // 初次調用 ? if($this->_having_str == "") { $this->_having_str = " HAVING "; } else { $this->_having_str .= " ".$operator." "; } // 和 where 子句一樣進行條件構造 $this->_condition_constructor(func_num_args(), func_get_args(), $this->_having_str); return $this; } public function orHaving() { $operator = "OR"; if($this->_having_str == "") { $this->_having_str = " HAVING "; } else { $this->_having_str .= " ".$operator." "; } $this->_condition_constructor(func_num_args(), func_get_args(), $this->_having_str); return $this; }
這里我們也留一個處理原生字符串的 havingRaw() 方法 (手動填寫數據,不進行數據綁定):
public function havingRaw($string) { $this->_having_str = " HAVING ".$string." "; return $this; }
構造 SQL SELECT id, SUM(price) FROM test_table GROUP BY price HAVING SUM(price) > 1000;:
$results = $driver->table("test_table") ->select("id", "SUM(price)") ->groupBy("price") ->having("SUM(price)", ">", 1000) ->get(); // 使用 havingRaw() $results = $driver->table("test_table") ->select("id", "SUM(price)") ->groupBy("price") ->havingRaw("SUM(price) > 1000") ->get();排序
排序就很簡單了,固定的語法和正序、倒序兩個模式,多個字段排序時使用逗號隔開:
public function orderBy($field, $mode = "ASC") { $mode = strtoupper($mode); if ( ! in_array($mode, ["ASC", "DESC"])) { throw new InvalidArgumentException("Error order by mode"); } // 初次調用? if($this->_orderby_str == "") { $this->_orderby_str = " ORDER BY ".self::_wrapRow($field)." ".$mode; } else { // 多個排序字段時,逗號隔開 $this->_orderby_str .= " , ".self::_wrapRow($field)." ".$mode; } return $this; }
構造 SQL SELECT * FROM tes_table ORDER BY price DESC, id ASC;:
$results = $driver->table("test_table") ->orderBy("price", "DESC") ->orderBy("id", "ASC") ->get();limit 和 分頁 limit 子句
標準 SQL 中的 limit 子句是 limit、offset 關鍵字一起使用的,Mysql 中有 LIMIT 0, 10 的簡寫形式,但是 PostgreSql 和 Sqlite 并不適用。所以我們選用 limit、offset 語法:
public function limit($offset, $step) { $this->_limit_str = " LIMIT ".$step." OFFSET ".$offset." "; return $this; }
構造 SQL SELECT * FROM test_table LIMIT 10 OFFSET 1;:
$results = $driver->table("test_table") ->limit(1, 10) ->get();分頁
在數據請求時會遇到請求資源數據量大的問題,對于這個問題,普遍的解決方案就是分頁。
有了 limit() 方法,可以進行分頁功能的實現了。
為了靈活的訪問分頁,我們要返回以下的數據:
數據的總數
每頁的數據個數
當前頁
下一頁
上一頁
第一頁
最后一頁
當前頁的數據集合
對于獲取數據的總數,這里有一些問題。
如何獲取總數?當然是使用上面講到的聚合函數 count() 來處理。但是,使用了 count() 方法后相當于進行了一次 SQL 的構造和執行 (執行后會將構造字符串設置為初始狀態),那么還如何進行當頁數據集合的獲取呢?
兩次執行
當然是有解決方案的,就是構造兩次。
我們的查詢構造器結構每次新建一個實例就會得到一個 PDO 的連接,所以為了構造兩次而新建實例的話,代價太大,那么如何在一個實例中實現兩次執行?
回顧上一篇,含有子查詢的 SQL 構造時經過了兩次構造,對構造字符串進行了保護、恢復。那么換到分頁中,還是構造兩次 SQL,對構造字符串進行保護、恢復,區別就是:因為要執行兩次,所以要對綁定的數據也進行保護、恢復。
基類中添加綁定數據保存、恢復的方法:
// 保存綁定數據 protected function _storeBindParam() { return $this->_bind_params; } // 恢復綁定數據 protected function _reStoreBindParam($bind_params) { $this->_bind_params = $bind_params; }
基類中添加 paginate() 方法:
public function paginate($step, $page = NULL) { // 保存構造字符串和綁定數據 $store = $this->_storeBuildAttr(); $bind_params = $this->_storeBindParam(); // 獲取數據總數 $count = $this->count(); // 恢復構造字符串和綁定數據 $this->_reStoreBuildAttr($store); $this->_reStoreBindParam($bind_params); // 創建分頁數據 $page = $page ? $page : 1; // 第一頁 $this->limit($step * ($page - 1), $step); $rst["total"] = $count; $rst["per_page"] = $step; $rst["current_page"] = $page; $rst["next_page"] = ($page + 1) > ($count / $step) ? NULL : ($page + 1); $rst["prev_page"] = ($page - 1) < 1 ? NULL : ($page - 1); $rst["first_page"] = 1; $rst["last_page"] = $count / $step; $rst["data"] = $this->get(); // 返回結果 return $rst; }
測試一下,獲取從第五頁的數據,每頁 10 條數據:
$results = $driver->table("test_table") ->paginate(10, 5);
Just do it!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/30826.html
摘要:而在項目開發中,我們想要的是一個更好用的可維護的工具,此時,對代碼的封裝模塊化就顯得尤為重要,于是出現了兩種方案查詢構造器,對象關系映射。典型環境下按照一般的查詢構造器處理就行。 文章目錄 寫一個特殊的查詢構造器 - (前言) 寫一個特殊的查詢構造器 - (一、程序結構,基礎封裝) 寫一個特殊的查詢構造器 - (二、第一條語句) 寫一個特殊的查詢構造器 - (三、條件查詢) 寫一個特殊...
閱讀 3585·2023-04-26 01:43
閱讀 2971·2021-10-14 09:42
閱讀 5404·2021-09-30 09:59
閱讀 2172·2021-09-04 16:40
閱讀 1208·2019-08-30 15:52
閱讀 822·2019-08-29 17:09
閱讀 1993·2019-08-26 13:37
閱讀 3432·2019-08-26 10:20