BUG:Model类的select方法默认只返回1条记录

TP的手册中写的是select方法返回的是全部的记录,但我在实际操作中,返回的只有1条记录,我们再次进行源码的分析。

(1)Action中的代码如下:

 

 
  
  1. $operaInfo = new Model("tbloperainfo"); 
  2. $list = $operaInfo->select(); 

(2)根据代码我们进入Model类的select方法,在select方法中又调用了Db类的select方法:

 
  
  1. $resultSet = $this->db->select($options); 

(3)select方法中有如下的代码片段,对查询的sql进行了处理:

 
  
  1. $sql   = str_replace
  2.             array('%TABLE%','%DISTINCT%','%FIELDS%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%'), 
  3.             array
  4.                 $this->parseTable($options['table']), 
  5.                 $this->parseDistinct(isset($options['distinct'])?$options['distinct']:false), 
  6.                 $this->parseField(isset($options['field'])?$options['field']:'*'), 
  7.                 $this->parseJoin(isset($options['join'])?$options['join']:''), 
  8.                 $this->parseWhere(isset($options['where'])?$options['where']:''), 
  9.                 $this->parseGroup(isset($options['group'])?$options['group']:''), 
  10.                 $this->parseHaving(isset($options['having'])?$options['having']:''), 
  11.                 $this->parseOrder(isset($options['order'])?$options['order']:''), 
  12.                 $this->parseLimit(isset($options['limit'])?$options['limit']:''
  13.             ),$this->selectSql); 
  14.         $sql   .= $this->parseLock(isset($options['lock'])?$options['lock']:false); 
  15.         return $this->query($sql); 

在处理的时候调用了parseLimit方法,由于面向对象的多态性,这个方法应该是在DbMssql中的parseLimit方法。我们看下该方法的参数:isset($options['limit'])?$options['limit']:'' 这个表达式结果应该为空字符串'',因为我们在调用Model类的select方法的时候没有传任何参数。

我们进入DbMssql看下代码如下:

 
  
  1. public function parseLimit($limit) { 
  2.         if(empty($limit)) $limit=1; 
  3.         $limit  =   explode(',',$limit); 
  4.         if(count($limit)>1) 
  5.             $limitStr   =   '(T1.ROW_NUMBER BETWEEN '.$limit[0].' + 1 AND '.$limit[0].' + '.$limit[1].')'
  6.         else 
  7.             $limitStr = '(T1.ROW_NUMBER BETWEEN 1 AND '.$limit[0].")"
  8.         return $limitStr
  9.     } 

(4)在parseLimit方法中,第一步将$limit赋值为1;第二部将$limit拆分为数组;第三部判断数组的元素个数后执行了如下的代码:

$limitStr = '(T1.ROW_NUMBER BETWEEN 1 AND '.$limit[0].")"; 

在这里我们可以看出$limitStr的具体值为:'(T1.ROW_NUMBER BETWEEN 1 AND 1)'

好,我们继续跟踪源码。

(5)第3步对$selectSql处理后,调用了DbMssql类的query方法,在该方法中又调用了getAll方法将查询的结果存入$result数组,紧接着将$result一层层返回。

(6)好,到此为止执行流程已经全部结束。我们看下具体执行的SQL,如下:

 
  
  1. SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER ( ORDER BY agentid) AS ROW_NUMBER FROM (SELECT * FROM tblagentinfo) AS thinkphp) AS T1 WHERE (T1.ROW_NUMBER BETWEEN 1 AND 1) 

看下执行的SQL,发现引起这个BUG的原因就是: (T1.ROW_NUMBER BETWEEN 1 AND 1) 这个SQL片段。

(7)原因找到了,该如何处理呢?

我们肯定是在DbMssql类的parseLimit方法下手,因为我们需要查询所有的记录,因此该方法返回的值最好是为 '' 。我们将parseLimit方法的第一行代码修改为如下的代码:

 
  
  1. if(emptyempty($limit)) { 
  2.      return $limit

就是如果$limit为空,直接返回。修改后刷新下页面发现报错了,看了执行的SQL如下:

 
  
  1. SELECT T1.* FROM (SELECT thinkphp.*, ROW_NUMBER() OVER ( ORDER BY agentid) AS ROW_NUMBER FROM (SELECT * FROM tblagentinfo) AS thinkphp) AS T1 WHERE 

发现SQL到WHERE关键字就没了,我们看下DbMssql中$selectSql的定义:

 
  
  1. protected $selectSql  =     'SELECT T1.* FROM (SELECT ROW_NUMBER() OVER (%ORDER%) AS ROW_NUMBER, thinkphp.* FROM (SELECT %DISTINCT% %FIELDS% FROM %TABLE%%JOIN%%WHERE%%GROUP%%HAVING%) AS thinkphp) AS T1 WHERE %LIMIT%' 

再结合parseLimit方法中的代码,具体处理方法如下:

1.将SQL中的WHERE删掉

2.在parseLimit中将代码修改如下:

 
  
  1. if(count($limit)>1) 
  2.            $limitStr    =   ' WHERE (T1.ROW_NUMBER BETWEEN '.$limit[0].' + 1 AND '.$limit[0].' + '.$limit[1].')'
  3.     else 
  4.            $limitStr = ' WHERE (T1.ROW_NUMBER BETWEEN 1 AND '.$limit[0].")"

即是将WHERE直接放到$limitStr中,这样就OK了。