特殊なクエリー・コンストラクタの書き込み-(5、集約関数、グループ化、ソート、ページング)


where関連の句の構造が完了したら、他の句を構築し続けます.この記事では,集約関数,パケット,ソートなどの句の構造を行う.
集約関数
SQLには、SUM、COUNT、AVGなどの集約関数と呼ばれる統計、要約のための関数があります.
select()メソッドを使用する場合、select('COUNT(id))という書き方で集約関数を使用することができますが、この方法には欠点があります.
  • 意味的には直感的ではない
  • キーワードの競合を防ぐには、手動で引用符を追加する必要があります(3番目の条件クエリーを参照).
  • 集約データを取得するには、
  • を呼び出す煩雑な方法が必要である.
    集約データをより容易に得るためには、単独で作成する方法が必要です.
    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
    groupby句は、グループ化するフィールドを1つ以上(カンマで区切られた)指定します.
    having句はgroupby句とともに用いられ,パケットのフィルタ条件として空であり,キーワードを除いて文法と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();

    ツールバーの
    並べ替えは簡単です.固定文法と正順、逆順の2つのモードで、複数のフィールドを並べ替えるときにカンマで区切ります.
    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()メソッドがあれば、ページング機能の実現が可能になります.
    柔軟なアクセスのために、次のデータを返します.
  • データの合計
  • ページあたりのデータ数
  • 現在のページ
  • 次ページ
  • 前ページ
  • 第1ページ
  • 最終ページ
  • 現在のページのデータセット
  • 取得したデータの合計数について、いくつかの問題があります.
    合計の取得方法もちろん,上述した集約関数count()を用いて処理する.しかし、count()メソッドを使用するとSQLの構築と実行が1回行われる(実行すると構築文字列が初期状態に設定される)に相当しますが、ページデータセットの取得はどのように行われますか?
    2回の実行
    もちろん解決策はありますが、2回構築します.
    我々のクエリー・コンストラクタ構造は、インスタンスを新規作成するたびにPDOの接続を得るので、2回作成するためにインスタンスを新規作成すると、コストがかかりすぎます.では、1つのインスタンスで2回実行するにはどうすればいいですか.
    前編を振り返ると,サブクエリを含むSQL構造は2回の構造を経て,構造文字列を保護・復元した.では、ページングに変更するか、SQLを2回構築するか、構造文字列を保護、復元します.違いは、2回実行するため、バインドされたデータも保護、復元することです.
    ベースクラスにバインドデータの保存、復元を追加する方法:
    //       
    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;
    }

    テストして、5ページ目のデータを取得します.1ページあたり10個のデータを取得します.
    
    $results = $driver->table('test_table')
                ->paginate(10, 5);

    Just do it!