上一篇中我們掌握了基本的資料CURD方法,但更多的情況下面,由於商務邏輯的差異,CURD操作往往不是那麼簡單,尤其是複雜的商務邏輯下面,這也是ActiveRecord模式的不足之處。ThinkPHP的查詢語言配合連貫操作可以很好解決複雜的商務邏輯需求,本篇我們就首先來深入瞭解下架構的查詢語言。
介紹
ThinkPHP內建了非常靈活的查詢方法,可以快速的進行資料查詢操作,查詢條件可以用於讀取、更新和刪除等操作,主要涉及到where方法等連貫操作即可,無論是採用什麼資料庫,你幾乎採用一樣的查詢方法(個別資料庫例如Mongo在運算式查詢方面會有所差異),系統幫你解決了不同資料庫的差異性,因此我們把架構的這一查詢方式稱之為查詢語言。查詢語言也是ThinkPHP架構的ORM亮點,讓查詢操作更加簡單易懂。下面來一一講解查詢語言的內涵。
查詢方式
ThinkPHP可以支援直接使用字串作為查詢條件,但是大多數情況推薦使用索引數組或者對象來作為查詢條件,因為會更加安全。一、使用字串作為查詢條件這是最傳統的方式,但是安全性不高,例如:
- $User = M("User"); // 執行個體化User對象
- $User->where('type=1 AND status=1')->select();
最後產生的SQL語句是
- SELECT * FROM think_user WHERE type=1 AND status=1
採用字串查詢的時候,我們可以配合使用新版提供的字串條件的安全預先處理機制,暫且不再細說。二、使用數組作為查詢條件這種方式是最常用的查詢方式,例如:
- $User = M("User"); // 執行個體化User對象
- $condition['name'] = 'thinkphp';
- $condition['status'] = 1;
- // 把查詢條件傳入查詢方法
- $User->where($condition)->select();
最後產生的SQL語句是
- SELECT * FROM think_user WHERE `name`='thinkphp' AND status=1
如果進行多欄位查詢,那麼欄位之間的預設邏輯關係是 邏輯與 AND,但是用下面的規則可以更改預設的邏輯判斷,通過使用 _logic 定義查詢邏輯:
- $User = M("User"); // 執行個體化User對象
- $condition['name'] = 'thinkphp';
- $condition['account'] = 'thinkphp';
- $condition['_logic'] = 'OR';
- // 把查詢條件傳入查詢方法
- $User->where($condition)->select();
最後產生的SQL語句是
- SELECT * FROM think_user WHERE `name`='thinkphp' OR `account`='thinkphp'
三、使用對象方式來查詢 這裡以stdClass內建對象為例:
- $User = M("User"); // 執行個體化User對象
- // 定義查詢條件
- $condition = new stdClass();
- $condition->name = 'thinkphp';
- $condition->status= 1;
- $User->where($condition)->select();
最後產生的SQL語句和上面一樣
- SELECT * FROM think_user WHERE `name`='thinkphp' AND status=1
使用對象方式查詢和使用數組查詢的效果是相同的,並且是可以互換的,大多數情況下,我們建議採用數組方式更加高效。
運算式查詢
上面的查詢條件僅僅是一個簡單的相等判斷,可以使用查詢運算式支援更多的SQL查詢文法,也是ThinkPHP查詢語言的精髓,查詢運算式的使用格式:$map['欄位名'] = array('運算式','查詢條件');運算式不分大小寫,支援的查詢運算式有下面幾種,分別表示的含義是:
| 運算式 |
含義 |
| EQ |
等於(=) |
| NEQ |
不等於(<>) |
| GT |
大於(>) |
| EGT |
大於等於(>=) |
| LT |
小於(<) |
| ELT |
小於等於(<=) |
| LIKE |
模糊查詢 |
| [NOT] BETWEEN |
(不在)區間查詢 |
| [NOT] IN |
(不在)IN 查詢 |
| EXP |
運算式查詢,支援SQL文法 |
樣本如下:EQ :等於(=)
例如:
- $map['id'] = array('eq',100);
和下面的查詢等效
- $map['id'] = 100;
表示的查詢條件就是 id = 100NEQ: 不等於(<>)
例如:
- $map['id'] = array('neq',100);
表示的查詢條件就是 id <> 100GT:大於(>)
例如:
- $map['id'] = array('gt',100);
表示的查詢條件就是 id > 100EGT:大於等於(>=)
例如:
- $map['id'] = array('egt',100);
表示的查詢條件就是 id >= 100LT:小於(<)
例如:
- $map['id'] = array('lt',100);
表示的查詢條件就是 id < 100ELT: 小於等於(<=)
例如:
- $map['id'] = array('elt',100);
表示的查詢條件就是 id <= 100[NOT] LIKE: 同sql的LIKE
例如:
- $map['name'] = array('like','thinkphp%');
查詢條件就變成 name like 'thinkphp%'
如果配置了DB_LIKE_FIELDS參數的話,某些欄位也會自動進行模糊查詢。例如設定了:
- 'DB_LIKE_FIELDS'=>'titlecontent'
的話,使用
- $map['title'] = 'thinkphp';
查詢條件就會變成 title like '%thinkphp%'
支援數組方式,例如
- $map['a'] =array('like',array('%thinkphp%','%tp'),'OR');
- $map['b'] =array('notlike',array('%thinkphp%','%tp'),'AND');
產生的查詢條件就是:
- (a like '%thinkphp%' OR a like '%tp') AND (b not like '%thinkphp%' AND b not like '%tp')
[NOT] BETWEEN :同sql的[not] between, 查詢條件支援字串或者數組,例如:
- $map['id'] = array('between','1,8');
和下面的等效:
- $map['id'] = array('between',array('1','8'));
查詢條件就變成 id BETWEEN 1 AND 8[NOT] IN: 同sql的[not] in ,查詢條件支援字串或者數組,例如:
- $map['id'] = array('not in','1,5,8');
和下面的等效:
- $map['id'] = array('not in',array('1','5','8'));
查詢條件就變成 id NOT IN (1,5, 8)EXP:運算式,支援更複雜的查詢情況
例如:
- $map['id'] = array('in','1,3,8');
可以改成:
- $map['id'] = array('exp',' IN (1,3,8) ');
exp查詢的條件不會被當成字串,所以後面的查詢條件可以使用任何SQL支援的文法,包括使用函數和欄位名稱。查詢運算式不僅可用於查詢條件,也可以用於資料更新,例如:
- $User = M("User"); // 執行個體化User對象
- // 要修改的資料對象屬性賦值
- $data['name'] = 'ThinkPHP';
- $data['score'] = array('exp','score+1');// 使用者的積分加1
- $User->where('id=5')->save($data); // 根據條件儲存修改的資料
快捷查詢
從3.0版本開始,增加了快捷查詢方式,可以進一步簡化查詢條件的寫法,例如:一、實現不同欄位相同的查詢條件
- $User = M("User"); // 執行個體化User對象
- $map['nametitle'] = 'thinkphp';
- // 把查詢條件傳入查詢方法
- $User->where($map)->select();
查詢條件就變成
- name= 'thinkphp' OR title = 'thinkphp'
二、實現不同欄位不同的查詢條件
- $User = M("User"); // 執行個體化User對象
- $map['status&title'] =array('1','thinkphp','_multi'=>true);
- // 把查詢條件傳入查詢方法
- $User->where($map)->select();
'_multi'=>true必須加在數組的最後,表示當前是多條件匹配,這樣查詢條件就變成
- status= 1 AND title = 'thinkphp'
,查詢欄位支援更多的,例如:
- $map['status&score&title'] =array('1',array('gt','0'),'thinkphp','_multi'=>true);
查詢條件就變成
- status= 1 AND score >0 AND title = 'thinkphp'
注意:快捷查詢方式中“”和“&”不能同時使用。
區間查詢
ThinkPHP支援對某個欄位的區間查詢,例如:
- $map['id'] = array(array('gt',1),array('lt',10)) ;
得到的查詢條件是:
- (`id` > 1) AND (`id` < 10)
- $map['id'] = array(array('gt',3),array('lt',10), 'or') ;
得到的查詢條件是: (`id` > 3) OR (`id` < 10)
- $map['id'] = array(array('neq',6),array('gt',3),'and');
得到的查詢條件是:(`id` != 6) AND (`id` > 3)
最後一個可以是AND、 OR或者 XOR運算子,如果不寫,預設是AND運算。
區間查詢的條件可以支援普通查詢的所有運算式,也就是說類似LIKE、GT和EXP這樣的運算式都可以支援。另外區間查詢還可以支援更多的條件,只要是針對一個欄位的條件都可以寫到一起,例如:
- $map['name'] = array(array('like','%a%'), array('like','%b%'), array('like','%c%'), 'ThinkPHP','or');
最後的查詢條件是:
- (`name` LIKE '%a%') OR (`name` LIKE '%b%') OR (`name` LIKE '%c%') OR (`name` = 'ThinkPHP')
組合查詢
組合查詢的主體還是採用數組方式查詢,只是加入了一些特殊的查詢支援,包括字串模式查詢(_string)、複合查詢(_complex)、請求字串查詢(_query),混合查詢中的特殊查詢每次查詢只能定義一個,由於採用數組的索引方式,索引相同的特殊查詢會被覆蓋。一、字串模式查詢(採用_string 作為查詢條件)
數組條件還可以和字串條件混合使用,例如:
- $User = M("User"); // 執行個體化User對象
- $map['id'] = array('neq',1);
- $map['name'] = 'ok';
- $map['_string'] = 'status=1 AND score>10';
- $User->where($map)->select();
最後得到的查詢條件就成了:
- ( `id` != 1 ) AND ( `name` = 'ok' ) AND ( status=1 AND score>10 )
二、請求字串查詢方式請求字串查詢是一種類似於URL傳參的方式,可以支援簡單的條件相等判斷。
- $map['id'] = array('gt','100');
- $map['_query'] = 'status=1&score=100&_logic=or';
得到的查詢條件是:
- `id`>100 AND (`status` = '1' OR `score` = '100')
三、複合查詢複合查詢相當於封裝了一個新的查詢條件,然後併入原來的查詢條件之中,所以可以完成比較複雜的查詢條件組裝。
例如:
- $where['name'] = array('like', '%thinkphp%');
- $where['title'] = array('like','%thinkphp%');
- $where['_logic'] = 'or';
- $map['_complex'] = $where;
- $map['id'] = array('gt',1);
查詢條件是
- ( id > 1) AND ( ( name like '%thinkphp%') OR ( title like '%thinkphp%') )
複合查詢使用了_complex作為子查詢條件來定義,配合之前的查詢方式,可以非常靈活的制定更加複雜的查詢條件。
很多查詢方式可以相互轉換,例如上面的查詢條件可以改成:
- $where['id'] = array('gt',1);
- $where['_string'] = ' (name like "%thinkphp%") OR ( title like "%thinkphp") ';
最後產生的SQL語句是一致的。
統計查詢
在應用中我們經常會用到一些統計資料,例如當前所有(或者滿足某些條件)的使用者數、所有使用者的最大積分、使用者的平均成績等等,ThinkPHP為這些統計操作提供了一系列的內建方法,包括:
| 方法 |
說明 |
| Count |
統計數量,參數是要統計的欄位名(可選) |
| Max |
擷取最大值,參數是要統計的欄位名(必須) |
| Min |
擷取最小值,參數是要統計的欄位名(必須) |
| Avg |
擷取平均值,參數是要統計的欄位名(必須) |
| Sum |
擷取總分,參數是要統計的欄位名(必須) |
用法樣本:
- $User = M("User"); // 執行個體化User對象
擷取使用者數:
- $userCount = $User->count();
或者根據欄位統計:
- $userCount = $User->count("id");
擷取使用者的最大積分:
- $maxScore = $User->max('score');
擷取積分大於0的使用者的最小積分:
- $minScore = $User->where('score>0')->min('score');
擷取使用者的平均積分:
- $avgScore = $User->avg('score');
統計使用者的總成績:
- $sumScore = $User->sum('score');
並且所有的統計查詢均支援連貫操作的使用。
SQL查詢
ThinkPHP內建的ORM和ActiveRecord模式實現了方便的資料存取操作,而且新版增加的連貫操作功能更是讓這個資料操作更加清晰,但是ThinkPHP仍然保留了原生的SQL查詢和執行操作支援,為了滿足複雜查詢的需要和一些特殊的資料操作,SQL查詢的傳回值因為是直接返回的Db類的查詢結果,沒有做任何的處理。主要包括下面兩個方法:
1、query方法
| query 執行SQL查詢操作 |
| 用法 |
query($sql,$parse=false) |
| 參數 |
sql(必須):要查詢的SQL語句 parse(可選):是否需要解析SQL |
| 傳回值 |
如果資料非法或者查詢錯誤則返回false 否則返回查詢結果資料集(同select方法) |
使用樣本:
- $Model = new Model() // 執行個體化一個model對象 沒有對應任何資料表
- $Model->query("select * from think_user where status=1");
如果你當前採用了分散式資料庫,並且設定了讀寫分離的話,query方法始終是在讀伺服器執行,因此query方法對應的都是讀操作,而不管你的SQL語句是什麼。2、execute方法
| execute用於更新和寫入資料的sql操作 |
| 用法 |
execute($sql,$parse=false) |
| 參數 |
sql(必須):要執行的SQL語句 parse(可選):是否需要解析SQL |
| 傳回值 |
如果資料非法或者查詢錯誤則返回false 否則返回影響的記錄數 |
使用樣本:
- $Model = new Model() // 執行個體化一個model對象 沒有對應任何資料表
- $Model->execute("update think_user set name='thinkPHP' where status=1");
如果你當前採用了分散式資料庫,並且設定了讀寫分離的話,execute方法始終是在寫伺服器執行,因此execute方法對應的都是寫操作,而不管你的SQL語句是什麼。
動態查詢
藉助PHP5語言的特性,ThinkPHP實現了動態查詢,核心模型的動態查詢方法包括下面幾種:
| 方法名 |
說明 |
舉例 |
| getBy |
根據欄位的值查詢資料 |
例如,getByName,getByEmail |
| getFieldBy |
根據欄位查詢並返回某個欄位的值 |
例如,getFieldByName |
一、getBy動態查詢該查詢方式針對資料表的欄位進行查詢。例如,User對象擁有id,name,email,address 等屬性,那麼我們就可以使用下面的查詢方法來直接根據某個屬性來查詢合格記錄。
- $user = $User->getByName('liu21st');
- $user = $User->getByEmail('liu21st@gmail.com');
- $user = $User->getByAddress('中國深圳');
暫時不支援多資料欄位的動態查詢方法,請使用find方法和select方法進行查詢。二、getFieldBy動態查詢針對某個欄位查詢並返回某個欄位的值,例如
- $userId = $User->getFieldByName('liu21st','id');
表示根據使用者的name擷取使用者的id值。
子查詢
從3.0版本開始新增了子查詢支援,有兩種使用方式:1、使用select方法當select方法的參數為false的時候,表示不進行查詢只是返回構建SQL,例如:
- // 首先構造子查詢SQL
- $subQuery = $model->field('id,name')->table('tablename')->group('field')->where($where)->order('status')->select(false);
當select方法傳入false參數的時候,表示不執行當前查詢,而只是產生查詢SQL。2、使用buildSql方法
- $subQuery = $model->field('id,name')->table('tablename')->group('field')->where($where)->order('status')->buildSql();
調用buildSql方法後不會進行實際的查詢操作,而只是產生該次查詢的SQL語句(為了避免混淆,會在SQL兩邊加上括弧),然後我們直接在後續的查詢中直接調用。
- // 利用子查詢進行查詢
- $model->table($subQuery.' a')->where()->order()->select()
構造的子查詢SQL可用於ThinkPHP的連貫操作方法,例如table where等。
總結
本篇主要協助我們瞭解如何進行資料的查詢,包括簡單查詢、運算式查詢、快捷查詢、區間查詢、統計查詢,以及如何進行子查詢操作。後面我們還會詳細瞭解如何使用連貫操作進行更複雜的CURD操作。