where相关子句构造完成后,我们继续构造其他子句。在本文中,我们构建了聚合函数、分组、排序等子句。聚合函数在SQL中,有一些用于统计和汇总的函数称为聚合函数,如SUM、COUNT、AVG等。在使用select()方法时,我们可以使用select('COUNT(id)')来使用聚合函数,但这种方法有缺点:语义不直观,需要手动加上引号以防止关键字冲突(参见第3部分条件查询)获取聚合数据需要一系列繁琐的方法调用。为了更方便的获取聚合数据,我们需要为它单独写一个方法。获取某列的getList()方法可以通过PDO::FETCH_COLUMN来完成。将getList()方法添加到基类:publicfunctiongetList($field){$this->_cols_str=''.self::_quote($field).'';$this->_buildQuery();$this->_execute();//获取一列数据return$this->_pdoSt->fetchAll(PDO::FETCH_COLUMN,0);}count()方法基类添加count()方法:publicfunctioncount($field='*'){//确定是否*//为字段添加引号ifnot*if(trim($field)!='*'){$field=self::_quote($field);}//构造列查询字符串$this->_cols_str='COUNT('.$field.')AScount_num';//获取结果return$this->row()['count_num'];}构造SQLSELECTCOUNT(id)FROMtest_table;$results=$driver->table('test_table')->count('id');由于聚合函数方法和get()、row()方法一样获取结果,调用chain的时候记得放在最后执行。其他方法如sum()和avg()没有COUNT('*')这样的场景,比count()方法更简单。sum()方法:publicfunctionsum($field){$this->_cols_str='SUM('.self::_quote($field).')ASsum_num';return$this->row()['sum_num'];}avg(),max()等其他方法就不一一展示了。代码请参考WorkerF-聚合函数。分组:groupby和havinggroupby子句指定要分组的字段,可以是一个或多个(以逗号分隔)。having子句与groupby子句一起作为分组的过滤条件使用,可以为空。除关键字外,语法与where子句基本相同。基类新增groupBy()方法:publicfunctiongroupBy($field){//是第一次调用吗?if($this->_groupby_str==''){$this->_groupby_str='GROUPBY'.self::_wrapRow($field);}else{//非初始调用(多个分组字段),以逗号分隔$this->_groupby_str.=','.self::_wrapRow($field);}return$this;}向基类添加了having()和orHaving()方法:publicfunctionhaving(){$operator='AND';//初始调用?如果($this->_having_str==''){$this->_having_str='有';}else{$this->_having_str.=''.$operator.'';}//像where子句一样的条件构造$this->_condition_constructor(func_num_args(),func_get_args(),$this->_having_str);返回$this;}公共函数orHaving(){$operator='OR';如果($this->_having_str==''){$this->_having_str='有';}else{$this->_having_str.=''.$operator.'';}$this->_condition_constructor(func_num_args(),func_get_args(),$this->_having_str);return$this;}这里我们还留了一个处理原生字符串的havingRaw()方法(手动填入数据,不绑定数据):publicfunctionhavingRaw($string){$this->_having_str='HAVING'.$string.'';return$this;}构造SQLSELECTid,SUM(price)FROMtest_tableGROUPBYpriceHAVINGSUM(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();排序非常简单,语法固定,有正序和倒序两种方式。多个字段排序时,使用逗号分隔:publicfunctionorderBy($field,$mode='ASC'){$mode=strtoupper($mode);if(!in_array($mode,['ASC','DESC'])){thrownew\InvalidArgumentException("Errororderbymode");}//初始调用?如果($this->_orderby_str==''){$this->_orderby_str='ORDERBY'.self::_wrapRow($field).''.$模式;}else{//对于多个排序字段,逗号分隔$this->_orderby_str.=','.self::_wrapRow($field).''.$模式;}return$this;}构造SQLSELECT*FROMtes_tableORDERBYpriceDESC,idASC;:$results=$driver->table('test_table')->orderBy('price','DESC')->orderBy('id','ASC')->get();limit和paginationlimit子句标准SQL中的limit子句是和limit和offset关键字一起使用的,Mysql有LIMIT0,10的简写形式,但是PostgreSql和Sqlite不适用,所以我们选择limit和offset语法:publicfunctionlimit($offset,$step){$this->_limit_str='LIMIT'.$step.'OFFSET'.$offset.'';return$this;}构造SQLSELECT*FROMtest_tableLIMIT10OFFSET1;:$results=$driver->table('test_table')->limit(1,10)->get();Paging在请求数据的时候会遇到请求资源数据量大的问题。对于这个问题,常见的解决方案是分页。通过limit()方法可以实现分页功能。为了灵活的访问分页,我们需要返回如下数据:总数据条数、每页数据条数、当前页、下一页、上一页、第一页、最后一页、当前页面的数据集。对于获得的数据总数,这里有一些问题。如何获得总数?当然是使用上面提到的聚合函数count()来处理的。但是,使用count()方法相当于构造并执行一条SQL(执行后构造字符串会置为初始状态),那么如何获取当前页面的数据集呢?当然,执行两次也是有解决办法的,就是构造两次。每次我们创建一个我们的querybuilder结构的实例,我们都会得到一个PDO连接,那么如果我们为两个构造创建一个新的实例,成本太高了,那么如何在一个实例中实现两次执行呢?回顾上一篇,包含子查询的SQL构造经历了两次构造,构造字符串被保护和恢复。然后切换到分页,或者二次构造SQL,保护和恢复构造好的字符串。不同的是:因为需要执行两次,所以绑定的数据也要进行保护和恢复。在基类中添加保存和恢复绑定数据的方法://保存绑定数据保护函数_storeBindParam(){return$this->_bind_params;}//恢复绑定数据保护函数_reStoreBindParam($bind_params){$this->_bind_params=$bind_params;}在基类中添加paginate()方法:publicfunctionpaginate($step,$page=NULL){//保存构造字符串和绑定数据$store=$this->_storeBuildAttr();$bind_params=$this->_storeBindParam();//获取总数据$count=$this->count();//恢复构造字符串和绑定数据$this->_reStoreBuildAttr($store);$this->_reStoreBindParam($bind_params);//创建分页数据$page=$page?$页:1;//第一页$this->limit($step*($page-1),$step);$rst['总计']=$count;$rst['per_page']=$step;$rst['current_page']=$page;$rst['next_page']=($page+1)>($count/$step)?空值:($page+1);$rst['prev_page']=($page-1)<1?空:($页-1);$rst['第一页']=1;$rst['last_page']=$count/$step;$rst['数据']=$this->get();//返回结果return$rst;}测试一下,从第五页开始获取数据,每页10条数据:$results=$driver->table('test_table')->paginate(10,5);去做就对了!
