BUG:Model类的select方法默认只返回1条记录
TP的手册中写的是select方法返回的是全部的记录,但我在实际操作中,返回的只有1条记录,我们再次进行源码的分析。
(1)Action中的代码如下:
- $operaInfo = new Model("tbloperainfo");
- $list = $operaInfo->select();
(2)根据代码我们进入Model类的select方法,在select方法中又调用了Db类的select方法:
- $resultSet = $this->db->select($options);
(3)select方法中有如下的代码片段,对查询的sql进行了处理:
- $sql = str_replace(
- array('%TABLE%','%DISTINCT%','%FIELDS%','%JOIN%','%WHERE%','%GROUP%','%HAVING%','%ORDER%','%LIMIT%'),
- array(
- $this->parseTable($options['table']),
- $this->parseDistinct(isset($options['distinct'])?$options['distinct']:false),
- $this->parseField(isset($options['field'])?$options['field']:'*'),
- $this->parseJoin(isset($options['join'])?$options['join']:''),
- $this->parseWhere(isset($options['where'])?$options['where']:''),
- $this->parseGroup(isset($options['group'])?$options['group']:''),
- $this->parseHaving(isset($options['having'])?$options['having']:''),
- $this->parseOrder(isset($options['order'])?$options['order']:''),
- $this->parseLimit(isset($options['limit'])?$options['limit']:'')
- ),$this->selectSql);
- $sql .= $this->parseLock(isset($options['lock'])?$options['lock']:false);
- return $this->query($sql);
在处理的时候调用了parseLimit方法,由于面向对象的多态性,这个方法应该是在DbMssql中的parseLimit方法。我们看下该方法的参数:isset($options['limit'])?$options['limit']:'' 这个表达式结果应该为空字符串'',因为我们在调用Model类的select方法的时候没有传任何参数。
我们进入DbMssql看下代码如下:
- public function parseLimit($limit) {
- if(empty($limit)) $limit=1;
- $limit = explode(',',$limit);
- if(count($limit)>1)
- $limitStr = '(T1.ROW_NUMBER BETWEEN '.$limit[0].' + 1 AND '.$limit[0].' + '.$limit[1].')';
- else
- $limitStr = '(T1.ROW_NUMBER BETWEEN 1 AND '.$limit[0].")";
- return $limitStr;
- }
(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,如下:
- 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方法的第一行代码修改为如下的代码:
- if(emptyempty($limit)) {
- return $limit;
- }
就是如果$limit为空,直接返回。修改后刷新下页面发现报错了,看了执行的SQL如下:
- 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的定义:
- 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中将代码修改如下:
- if(count($limit)>1)
- $limitStr = ' WHERE (T1.ROW_NUMBER BETWEEN '.$limit[0].' + 1 AND '.$limit[0].' + '.$limit[1].')';
- else
- $limitStr = ' WHERE (T1.ROW_NUMBER BETWEEN 1 AND '.$limit[0].")";
即是将WHERE直接放到$limitStr中,这样就OK了。
转载于:https://blog.51cto.com/shane/778063