国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

寫一個“特殊”的查詢構造器 - (五、聚合函數、分組、排序、分頁)

iamyoung001 / 3217人閱讀

摘要:聚合函數在中,有一些用來統計匯總的函數,被稱作聚合函數,如等。方法其它方法如之類的編寫就不一一展示了,代碼請看聚合函數。如何獲取總數當然是使用上面講到的聚合函數來處理。

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 和 having

group 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

相關文章

  • 一個特殊查詢造器 - (前言)

    摘要:而在項目開發中,我們想要的是一個更好用的可維護的工具,此時,對代碼的封裝模塊化就顯得尤為重要,于是出現了兩種方案查詢構造器,對象關系映射。典型環境下按照一般的查詢構造器處理就行。 文章目錄 寫一個特殊的查詢構造器 - (前言) 寫一個特殊的查詢構造器 - (一、程序結構,基礎封裝) 寫一個特殊的查詢構造器 - (二、第一條語句) 寫一個特殊的查詢構造器 - (三、條件查詢) 寫一個特殊...

    GitChat 評論0 收藏0

發表評論

0條評論

iamyoung001

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<