(一)關於ThinkPHP2.1版本操做MSSQL類的BUG--count統計結果異常

 最近在研究ThinkPHP,我用的版本是2.1,數據庫爲MSSQL2005,在按示例操做的時候出現了一個問題:php

BUG:Model類的對象使用count進行記錄統計的時候結果老是爲1sql

下面咱們分析下源碼,看看具體的執行流程:thinkphp

1.Action中的代碼以下數據庫

  
  
  
  
  1. $agentInfo = new Model("tblagentinfo"); 
  2. $agentInfoCount = $agentInfo->count();  

下面咱們一步步追蹤。數組

(1)首先咱們進入Model類的源碼,位於:ThinkPHP/Lib/Think/Core/Model.class.php 。ide

咱們搜索count方法。發現沒有這個方法的定義,但咱們能夠搜索到這麼一個方法__call。該方法是PHP5以後新增的魔術方法,做用就是若是調用類中不存在的方法,PHP會自動執行__call方法.具體用法這邊咱們不作介紹,能夠參考這裏:this

咱們看看__call中有以下的代碼片斷:spa

 

  
  
  
  
  1. elseif(in_array(strtolower($method),array('count','sum','min','max','avg'),true)){ 
  2.   // 統計查詢的實現 
  3.   $field =  isset($args[0])?$args[0]:'*'
  4.   return $this->getField(strtoupper($method).'('.$field.') AS tp_'.$method); 

咱們發如今調用Model類對象的count方法時,調用了Model對象的getField方法。根據代碼咱們能分析傳遞給getField方法的參數是個字符串:"count(*) as tp_count"調試

(2)咱們再進入getField方法看看。對象

咱們發現該方法中存在個if else語句,根據咱們傳入的參數如今執行的代碼以下:

  
  
  
  
  1. else{   // 查找一條記錄 
  2.         $options['limit'] = 1; 
  3.         $result = $this->db->select($options); 
  4.         if(!emptyempty($result)) { 
  5.             return reset($result[0]); 
  6.         } 

咱們發現這裏對$options變量作了個賦值而且又調用了Db類的select方法。其實這裏TP調用的應該是DbMssql類的select方法,由於DbMssql繼承自Db類,且沒有對Db類的select進行重寫,所以咱們進入Db類看下。

(3)在Db類的select方法中有以下的代碼片斷:

 

  
  
  
  
  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); 

這裏的代碼是對查詢SQL進行了拼湊,這裏你們咱們要留意$selectSql變量,這個變量實際是在DbMssql中的定義的。接着代碼又調用了query方法,在該方法中執行了具體的數據庫查詢操做,並將查詢結果傳入了該類中的getAll方法,在getAll方法中將查詢結果存於$result數組中進行了返回。

(4)到目前爲止執行的流程已經結束,下面就是將$result數組一層層的返回。咱們回到第2部的代碼片斷。在調用了Db類的select後對返回的$result進行了判斷並將最終的結果reset($result[0])返回給了Action。

(5)好,如今結果已經返回了,咱們看下具體的問題出如今哪。咱們開啓TP的調試功能找出TP執行的SQL的具體代碼,以下:

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

該語句執行的結果以下圖:

(6)到這裏問題出現的緣由已經呼之欲出了,就是由於第4步中返回的結果是第一個字段的值,所以不管你怎麼查詢,結果都是1.那咱們該如何處理呢?

個人處理方法以下:

將DbMsql類中全局變量$selectSql定義進行了修改,修改後的語句以下:

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

就是將查詢結果中tp_count字段放在第一個字段便可。

相關文章
相關標籤/搜索