本章主要來學(xué)習(xí)和使用查詢構(gòu)造器的用法,掌握查詢構(gòu)造器對于掌握數(shù)據(jù)庫和模型的查詢操作非常關(guān)鍵,學(xué)習(xí)內(nèi)容主要包含:
在第一章我們已經(jīng)學(xué)習(xí)了如何使用原生查詢,不過原生查詢的話就失去了數(shù)據(jù)庫抽象訪問層的意義了,所以數(shù)據(jù)庫抽象訪問層的優(yōu)勢就是使用查詢構(gòu)造器進(jìn)行查詢。
查詢構(gòu)造器就是利用查詢類和生成類完成最終的查詢語句構(gòu)造(注意這里的查詢是一個泛指,包括數(shù)據(jù)庫的讀寫操作)。由于生成類是由查詢類自動調(diào)用的,可以說查詢構(gòu)造器的用法其實就是查詢類的用法,因此首先我們必須清楚查詢類怎么調(diào)用或者說是在什么時候調(diào)用。
為了方便學(xué)習(xí)和掌握數(shù)據(jù)庫的用法,我們建議在開始學(xué)習(xí)后面的內(nèi)容之前,進(jìn)行如下設(shè)置。
應(yīng)用配置文件中開啟調(diào)試模式和頁面Trace,以及修改日志配置為error
和sql
日志單獨記錄(便于我們查錯和進(jìn)行SQL分析):
// 開啟應(yīng)用調(diào)試模式
'app_debug' => true,
// 開啟應(yīng)用Trace
'app_trace' => true,
'log' => [
// 日志記錄方式
'type' => 'File',
// error和sql日志單獨記錄
'apart_level' => ['error','sql'],
],
然后在數(shù)據(jù)庫配置文件開啟數(shù)據(jù)庫調(diào)試模式
// 數(shù)據(jù)庫調(diào)試模式
'debug' => true,
開啟數(shù)據(jù)庫調(diào)試模式和頁面Trace的目的是為了便于在頁面直觀的看到當(dāng)前請求的數(shù)據(jù)庫連接情況和執(zhí)行的SQL語句,另外所有的歷史查詢SQL都可以在
runtime\log\當(dāng)前日期\sql.log
中查看,本書不打算給出每個查詢用法的最終SQL語句,最好的學(xué)習(xí)方式是親自驗證每個查詢的SQL語句是什么。
本書后面的示例代碼大多數(shù)情況不會寫完整的控制器文件和方法(這不是本書的重點),只是局部關(guān)鍵代碼的實現(xiàn),也假設(shè)你在調(diào)用Db
類方法之前已經(jīng)使用use
引入了think\Db
類,請確保你已經(jīng)掌握控制器的用法以及如何訪問和測試(如果不清楚,請參考官方快速入門第三部控制器從入門到精通)。
創(chuàng)建查詢類
框架內(nèi)置的查詢類就是think\db\Query
類,使用查詢構(gòu)造器一般都是自動實例化查詢類,無需手動實例化。通過第一章的學(xué)習(xí)我們已經(jīng)知道調(diào)用Db
類的任何方法都會自動調(diào)用connect
方法返回連接對象實例,然后調(diào)用連接對象的查詢構(gòu)造器方法會自動實例化查詢類。
以查詢構(gòu)造器的table
方法調(diào)用為例,如果用下面方式調(diào)用:
// 調(diào)用Db類的table方法
Db::table('data');
實際解析過程相當(dāng)于調(diào)用連接類的table
方法:
// 先實例化連接類然后調(diào)用table方法
Db::connect()->table('data');
然后繼續(xù)調(diào)用連接類的getQuery
方法(該方法中會自動實例化查詢類)后再調(diào)用查詢類的table
方法:
// 先實例化連接類然后獲取查詢類實例,調(diào)用查詢類的table方法
Db::connect()->getQuery()->table('data');
雖然實際的調(diào)用過程如上所示,但內(nèi)部的調(diào)用過程被數(shù)據(jù)訪問層封裝并銜接了,你只需要知道并使用下面的調(diào)用方法(除非你需要動態(tài)切換數(shù)據(jù)庫連接則需要調(diào)用connect
方法先):
// 調(diào)用Db類的table方法
Db::table('data');
Db
類可以直接調(diào)用查詢類的方法,數(shù)據(jù)訪問層的細(xì)分只是一種內(nèi)部的架構(gòu)設(shè)計,對于開發(fā)者來說,你只需要明白一件事:數(shù)據(jù)庫操作就是用Db
類,至于內(nèi)部怎么相互調(diào)用完全不需要操心。
【5.1須知】
5.1 版本則簡化了調(diào)用流程,Db的靜態(tài)方法調(diào)用直接就是調(diào)用的查詢類的方法。也就是說,上面的方法其實就是調(diào)用了Query類的
table
方法,而沒有中間過程。
事實上,系統(tǒng)還額外提供了一個助手函數(shù)db
用于完成上述方法,所以
$db = db('data');
和前面的用法等效,但是——有一點必須引起重視。
db
助手函數(shù)默認(rèn)每次調(diào)用都會重新連接數(shù)據(jù)庫(目的是確保你的每次db
函數(shù)調(diào)用不會相互影響),你可以使用db('data',[],false)
方式解決。(好消息是V5.0.9+
版本已經(jīng)完美解決,無需這樣調(diào)用了)
了解了查詢類的調(diào)用機(jī)制后,后面我們就要逐步來講解查詢類的各種查詢方法了,查詢器類內(nèi)置了大量的查詢方法用于構(gòu)建查詢,如果你仍然覺得不夠用,可以自己擴(kuò)展然后使用下面的方式配置調(diào)用。
如果你自定義或者擴(kuò)展了核心的查詢類,那么可以在數(shù)據(jù)庫配置文件中設(shè)置:
// 定義數(shù)據(jù)庫的查詢類
'query' => '\\app\\db\\Query',
定義后,查詢構(gòu)造器就會自動調(diào)用app\db\Query
查詢類(一般來說會繼承核心的think\db\Query
類)。
數(shù)據(jù)庫CURD操作
使用查詢構(gòu)造器進(jìn)行查詢,起碼需要掌握查詢類的幾個關(guān)鍵的方法:
查詢方法 | 作用描述 |
---|---|
table | 指定查詢數(shù)據(jù)表 |
field | 指定查詢字段 |
where | 指定查詢條件 |
order | 指定結(jié)果排序 |
limit | 指定查詢結(jié)果數(shù) |
find | 查詢一條記錄 |
select | 查詢數(shù)據(jù)集 |
insert | 寫入數(shù)據(jù) |
update | 更新數(shù)據(jù) |
delete | 刪除數(shù)據(jù) |
這些方法對SQL稍微了解一點的用戶理解起來應(yīng)該不難,find
和select
方法的區(qū)別在于find
方法只是查詢一條記錄(即使?jié)M足條件的記錄有很多),并且返回的是一個一維數(shù)組(沒有結(jié)果返回Null
),而select
方法返回的是一個二維數(shù)組(沒有結(jié)果返回空數(shù)組),除此之外,他們的查詢語法都是相同的。
這些常用方法其實包含兩種大的類別,一個是輔助方法(輔助查詢用的,也稱為鏈?zhǔn)椒椒?,例?code>table、field
、where
、order
和limit
等方法),一個是真正的查詢方法(find
、select
、insert
、update
和delete
方法),查詢方法是必須的,而輔助方法是可選的,并且輔助方法必須在查詢方法之前被調(diào)用,并且在查詢調(diào)用之后自動失效。
【5.1須知】
5.1版本每次查詢完成后鏈?zhǔn)椒椒ǖ臈l件參數(shù)會保留。
關(guān)于
table
方法這里作出一個特別的說明,在新版框架的架構(gòu)設(shè)計規(guī)范中,我們建議數(shù)據(jù)表的命名不使用前綴設(shè)計,表前綴其實已經(jīng)是一種過時的設(shè)計了,很多時候跨庫的設(shè)計比表前綴的設(shè)計來的更靈活和實用,而且前綴設(shè)計(尤其是在混合用的情況下)帶來的一些困惑和問題卻是很多新手最大的苦惱,所以何必自尋煩惱(如果你一定要采用前綴設(shè)計,那么請用name
方法替代table
方法,并且在數(shù)據(jù)庫配置文件中配置prefix
參數(shù),我也不攔著你,哭的時候別找我_)。
使用查詢構(gòu)造器進(jìn)行查詢,能夠最大程度的避免寫針對特定數(shù)據(jù)庫的查詢語句,減少跨數(shù)據(jù)庫類型的遷移成本。
下面我們對數(shù)據(jù)庫的CURD
操作給出基本的用法。
創(chuàng)建(Create)
創(chuàng)建操作指往數(shù)據(jù)表添加新的記錄,下面是示例代碼:
// 插入單個記錄
Db::table('data')
->insert(['id' => 8, 'name' => 'thinkphp']);
// 插入多個記錄
Db::table('data')
->insertAll([
['id' => 9, 'name' => 'thinkphp'],
['id' => 10, 'name' => 'topthink'],
]);
insertAll
方法的數(shù)據(jù)集中的元素請確保字段列表一致,否則會出錯。
由于insert
和insertAll
方法最終都是調(diào)用連接類的execute
方法,我們已經(jīng)知道execute
方法的返回值是影響的記錄數(shù),所以insert
和insertAll
方法的返回值也是影響(新增)的記錄數(shù),并不會返回主鍵值。
主鍵id
如果是自增類型,可以使用:
// 插入單個記錄
Db::table('data')
->insert(['name' => 'kancloud']);
如果需要獲取自增id
的值,可以在insert
方法之后緊接著調(diào)用getLastInsID
方法:
// 插入單個記錄
Db::table('data')
->insert(['name' => 'kancloud']);
// 獲取上次寫入的自增Id
$id = Db::getLastInsID();
由于PDO內(nèi)部的原因,
insertAll
方法后調(diào)用getLastInsID
方法返回的自增Id可能存在偏差。
或者直接合并上面的代碼為:
// 插入單個記錄 并返回自增Id
$id = Db::table('data')
->insertGetId(['name' => 'kancloud']);
對于不在數(shù)據(jù)表中的字段寫入,系統(tǒng)默認(rèn)會拋出異常,但可以配合strict(false)
方法忽略錯誤繼續(xù)執(zhí)行,下面的test
數(shù)據(jù)會被忽略(data
表不存在test
字段)。
// 插入單個記錄 并返回自增Id
$id = Db::table('data')
->strict(false)
->insertGetId([
'name' => 'kancloud',
'email' => 'kancloud@qq.com',
'test' => '這個數(shù)據(jù)不會被寫入',
]);
你并不需要考慮寫入數(shù)據(jù)失敗的情況,數(shù)據(jù)庫操作過程有任何的錯誤都會拋出異常,你需要做的只是修正BUG或者捕獲異常自行處理。
更新(Update)
更新操作指更改數(shù)據(jù)表記錄的單個或者多個字段,下面是示例代碼:
// 更新記錄
Db::table('data')
->where('id', 8)
->update(['name' => "framework"]);
出于數(shù)據(jù)安全考慮,ThinkPHP的update
方法必須使用更新條件而不允許無條件更新,如果沒有指定更新條件,則會從更新數(shù)據(jù)中獲取主鍵作為更新條件,例如當(dāng)id
是主鍵的時候下面的寫法依然有效:
// 更新記錄
Db::table('data')
->update(['id' => 8, 'name' => "framework"]);
可以過濾需要更新的字段列表,例如只允許更新name
字段的值(假設(shè)data
表還存在email
字段)
// 更新記錄
Db::table('data')
->field(['name'])
->where('id', 8)
->update([
'name' => 'framework',
'email' => 'thinkphp@qq.com'
]);
實際更新的字段只有name
,email
字段的數(shù)據(jù)會被忽略。
一般來說,update
方法用于更新數(shù)據(jù)的多個字段,如果只是更新某個字段的值,也可以用setField
方法,例如:
// 更新記錄
Db::table('data')
->where('id', 8)
->setField('name','framework');
返回值和update
方法一致,因為setField
最終也是調(diào)用的update
方法。
對于數(shù)字類型的字段的步長更新,框架提供了兩個專門的方法用于遞增和遞減操作。
遞增操作:
// score 字段加 1
Db::table('user')
->where('id', 1)
->setInc('score');
// score 字段加 5
Db::table('user')
->where('id', 1)
->setInc('score', 5);
遞減操作:
// score 字段減 1
Db::table('user')
->where('id', 1)
->setDec('score');
// score 字段減 5
Db::table('user')
->where('id', 1)
->setDec('score', 5);
setInc
/setDec
支持延時寫入,延時寫入的含義是會把需要遞增/遞減的數(shù)據(jù)緩存起來(在緩存中進(jìn)行遞增和遞減操作),在達(dá)到指定的時間計時后才會把最終計算的緩存數(shù)據(jù)寫入數(shù)據(jù)庫,避免頻繁操作數(shù)據(jù)庫帶來的性能開銷,下例中延時10秒,給score
字段增加1:
Db::table('user')
->where('id', 1)
->setInc('score', 1, 10);
setInc
和setDec
可以同時使用延時寫入,系統(tǒng)會自動計算最終需要寫入數(shù)據(jù)庫的值。
讀?。≧ead)
讀取操作就是指對數(shù)據(jù)表(包括單表和多表)的各種查詢操作,該操作涉及的內(nèi)容和營養(yǎng)較多,也是數(shù)據(jù)庫操作中最復(fù)雜和最難掌握的,所以我們會陸續(xù)花大量的篇幅來講解,這里先簡單了解下幾個單表查詢最基礎(chǔ)的方法,用法示例:
查詢單個數(shù)據(jù):
// 查詢查個數(shù)據(jù)
$data = Db::table('data')
->where('id', 8)
->find();
dump($data);
// 簡化寫法,直接傳入主鍵查詢
$data = Db::table('data')
->find(8);
find
方法存在查詢結(jié)果的話返回一個數(shù)組,沒有的話返回null
,要獲取查詢記錄的值,可以使用數(shù)組方式操作:
echo $data['id'];
echo $data['name'];
查詢多個數(shù)據(jù):
// 查詢多個數(shù)據(jù)
$list = Db::table('data')
->where('id','in', [1, 5, 8])
->select();
dump($list);
// 直接傳入多個主鍵的值查詢
$list = Db::table('data')
->select([1,5,8]);
select
方法存在查詢結(jié)果的話返回一個二維數(shù)組,如果沒有數(shù)據(jù)則返回一個空數(shù)組。
可以遍歷獲取記錄的值,例如:
foreach ($list as $data) {
echo $data['name'];
}
我們修改數(shù)據(jù)庫配置文件中的resultset_type
值
// 數(shù)據(jù)集返回類型
'resultset_type' => 'collection',
select
方法的返回值就會變成一個數(shù)據(jù)集對象(think\Collection
)。
二維數(shù)組和數(shù)據(jù)集對象的區(qū)別在于,數(shù)據(jù)集對象提供了更多的內(nèi)置數(shù)據(jù)處理方法,但在基本使用上,這兩種方式?jīng)]有不同,很多開發(fā)者一看到對象的輸出信息就不知所措了,完全沒必要,除非你有“對象”恐懼癥,關(guān)于數(shù)據(jù)集的用法我們會在后續(xù)進(jìn)行詳細(xì)講解。
一個特殊的情況是,使用分頁查詢方法的話,無論數(shù)據(jù)庫配置返回數(shù)據(jù)集類型設(shè)置什么,都會返回分頁類
think\Paginator
的對象實例,用法基本上和數(shù)據(jù)集對象一致。
5.1版本和5.0的一個關(guān)鍵的區(qū)別,在于5.1版本開始,每次查詢完成后不會清空查詢條件,但每次Db類的靜態(tài)調(diào)用都是一個全新的查詢。
獲取記錄的某個字段值:
// 返回某個字段的值
Db::table('data')
->where('id', 1)
->value('name');
獲取記錄某個列的值:
// 獲取name列的數(shù)組
Db::table('data')
->where('status', 1)
->column('name');
// 指定索引
Db::table('data')
->where('status', 1)
->column('name', 'id');
column
的返回類型永遠(yuǎn)都是數(shù)組,不受resultset_type
參數(shù)的影響。
你可能現(xiàn)在會對where
方法的用法心存疑慮,確實查詢構(gòu)造器大部分的工作和復(fù)雜度都集中在where
方法,不過不用著急,請耐心往下看,在學(xué)習(xí)完本章和下一章的內(nèi)容后你基本上就會對where
方法駕輕就熟了。
刪除(Delete)
刪除操作指對數(shù)據(jù)表的單個記錄或者多個記錄的刪除操作,示例代碼如下:
// 刪除數(shù)據(jù)
Db::name('data')
->where('id', 18)
->delete();
和更新操作一樣,ThinkPHP不允許使用無條件刪除操作,如果不帶條件可以直接使用主鍵刪除:
// 刪除數(shù)據(jù)
Db::name('data')
->delete(18);
// 刪除多條數(shù)據(jù)
Db::name('data')
->delete([1, 5, 8]);
如果你確定要執(zhí)行無條件刪除操作, 可以使用下面的方式:
// 刪除所有數(shù)據(jù)
Db::name('data')
->delete(true);
事實上,對于業(yè)務(wù)數(shù)據(jù)表,基本上不建議使用刪除操作,而是使用軟刪除(邏輯刪除,其實執(zhí)行的是數(shù)據(jù)表的更新操作)替代實際的物理刪除。軟刪除屬于模型的功能設(shè)計,我們會在后面的章節(jié)給你講解。
使用鏈?zhǔn)椒椒?/h2>
掌握了基本的CURD操作后,我們就來熟悉下鏈?zhǔn)椒椒ǖ母拍?,其實就是前面我們提到的輔助查詢方法。首先鏈?zhǔn)椒椒ǖ哪康氖菫榱俗尣樵兏逦椭庇^,下面的兩個代碼實現(xiàn)哪個更清晰易懂大家可以比較下。
常規(guī)的方法實現(xiàn):
Db::table('data');
Db::where('id', '>', 1);
Db::limit(8);
$list = Db::select();
事實上,如果在5.1版本中,上面的用法是無效的。
使用鏈?zhǔn)椒椒▽崿F(xiàn):
$list = Db::table('data')
->where('id', '>', 1)
->limit(8)
->select();
事實上,我們前面使用的table
、where
、limit
之類的方法都稱之為鏈?zhǔn)椒椒ǎ鎰e一個方法是否屬于鏈?zhǔn)椒椒ǖ囊粋€顯著特征就是看這個方法是否返回當(dāng)前的對象實例。
正因為鏈?zhǔn)讲僮鞣椒ǚ祷氐氖钱?dāng)前對象實例,所以不同的鏈?zhǔn)椒椒ㄔ谡{(diào)用的順序上沒有先后的概念,但相同的鏈?zhǔn)椒椒ㄕ{(diào)用順序會影響最終的查詢,下面的兩個例子完全等效:
$list = Db::table('data')
->where('id', '>', 1)
->limit(8)
->select();
和
$list = Db::limit(8)
->where('id', '>', 1)
->table('data')
->select();
上面兩個查詢最終生成的SQL語句是完全相同的。
但下面兩個查詢最終生成的SQL是不同的
$list = Db::table('data')
->where('id', '>', 1)
->where('name', 'like', '%think%')
->order('id', 'desc')
->order('create_time', 'desc')
->limit(8)
->select();
和
$list = Db::table('data')
->where('name', 'like', '%think%')
->where('id', '>', 1)
->order('create_time', 'desc')
->order('id', 'desc')
->limit(8)
->select();
多個where
方法和order
方法的調(diào)用順序最終影響了生成的SQL語句,雖然有時候并不會影響查詢結(jié)果。上面的兩個例子where
方法的順序并不會影響查詢條件,而order
方法的順序則改變了最終數(shù)據(jù)的排序。
鏈?zhǔn)椒椒ǖ恼{(diào)用順序取決于你的思維習(xí)慣或者說團(tuán)隊規(guī)范。
查詢類的大部分方法都是采用鏈?zhǔn)椒椒▽崿F(xiàn),給你的CURD查詢帶來便利。
查詢類的所有鏈?zhǔn)椒椒ㄕ{(diào)用都不具備記憶性,每次查詢操作完成后幾乎所有的鏈?zhǔn)椒椒◣淼挠绊憣⒉粡?fù)存在,簡單來說就是鏈?zhǔn)椒椒ǖ慕Y(jié)果不會帶入后面的其它查詢。
系統(tǒng)支持的鏈?zhǔn)讲僮鞣椒ㄓ校?/p>
連貫操作 | 作用 | 支持的參數(shù)類型 |
---|---|---|
where* | 用于AND查詢 | 字符串、數(shù)組和對象 |
whereOr* | 用于OR查詢 | 字符串、數(shù)組和對象 |
whereXor* | 用于XOR查詢 | 字符串、數(shù)組和對象 |
whereTime* | 用于時間日期的快捷查詢 | 字符串 |
table | 用于定義要操作的數(shù)據(jù)表名稱 | 字符串和數(shù)組 |
alias | 用于給當(dāng)前數(shù)據(jù)表定義別名 | 字符串和數(shù)組 |
field* | 用于定義要查詢的字段(支持字段排除) | 字符串和數(shù)組 |
order* | 用于對結(jié)果排序 | 字符串和數(shù)組 |
limit | 用于限制查詢結(jié)果數(shù)量 | 字符串和數(shù)字 |
page | 用于查詢分頁(內(nèi)部會轉(zhuǎn)換成limit) | 字符串和數(shù)字 |
group | 用于對查詢的group支持 | 字符串 |
having | 用于對查詢的having支持 | 字符串 |
join* | 用于對查詢的join支持 | 字符串和數(shù)組 |
union* | 用于對查詢的union支持 | 字符串、數(shù)組和對象 |
view* | 用于視圖查詢 | 字符串、數(shù)組 |
distinct | 用于查詢的distinct支持 | 布爾值 |
lock | 用于數(shù)據(jù)庫的鎖機(jī)制 | 布爾值 |
cache | 用于查詢緩存 | 支持多個參數(shù) |
relation* | 用于關(guān)聯(lián)查詢 | 字符串 |
with* | 用于關(guān)聯(lián)預(yù)載入 | 字符串、數(shù)組 |
bind* | 用于數(shù)據(jù)綁定操作 | 數(shù)組或多個參數(shù) |
comment | 用于SQL注釋 | 字符串 |
force | 用于數(shù)據(jù)集的強(qiáng)制索引 | 字符串 |
master | 用于設(shè)置主服務(wù)器讀取數(shù)據(jù) | 布爾值 |
strict | 用于設(shè)置是否嚴(yán)格檢測字段名是否存在 | 布爾值 |
sequence | 用于設(shè)置Pgsql的自增序列名 | 字符串 |
failException | 用于設(shè)置沒有查詢到數(shù)據(jù)是否拋出異常 | 布爾值 |
partition | 用于設(shè)置分表信息 | 數(shù)組 字符串 |
data* | 用于設(shè)置寫入數(shù)據(jù)(5.0.5+ ) |
數(shù)組 |
inc* | 用于設(shè)置字段遞增 (5.0.5+ ) |
字符串 |
dec* | 用于設(shè)置字段遞減(5.0.5+ ) |
字符串 |
exp* | 用于設(shè)置SQL表達(dá)式(5.0.5+ ) |
字符串 |
其中帶*標(biāo)識的表示支持多次調(diào)用。
鑒于鏈?zhǔn)椒椒ㄓ泻芏?,而且手冊也有詳?xì)的描述(完全開發(fā)手冊對鏈?zhǔn)椒椒?/a>給出了詳細(xì)的解釋,可以參閱),這里想聲明幾點,避免產(chǎn)生困惑:
- 鏈?zhǔn)椒椒ㄖС炙械腃URD操作;
- 鏈?zhǔn)椒椒ū旧碇皇欠祷夭樵儗ο?,只有?zhí)行查詢后才會返回結(jié)果,而且只能在查詢方法之前被調(diào)用;
- 不同鏈?zhǔn)椒椒ǖ恼{(diào)用順序不影響查詢;
- 相同鏈?zhǔn)椒椒ǖ恼{(diào)用順序可能會影響查詢(至少會影響SQL語句)
- 鏈?zhǔn)椒椒ㄔ谕瓿刹樵兒髸詣邮В?/li>
- 同一個鏈?zhǔn)椒椒ㄔ贑URD操作中的作用可能不同;
- 鏈?zhǔn)椒椒▋H針對CURD方法,對原生查詢無效;
一個典型的例子就是field
方法,在查詢和寫入操作中的代表的作用完全不同。
查詢語言
在所有的鏈?zhǔn)椒椒ㄖ?,最?fù)雜的莫過于查詢條件方法以及和其它方法的配合。和查詢條件相關(guān)的用法我們稱之為查詢語言,查詢語言可以用于數(shù)據(jù)庫的URD
操作,要掌握查詢語言的核心,謹(jǐn)記:2個方法,3個用法,8個要訣。
2個方法
其中兩個方法是:
方法 | 描述 |
---|---|
where |
AND 條件查詢 |
whereOr |
OR 條件查詢 |
上面兩個方法以及和其它的鏈?zhǔn)椒椒ㄅ浜峡梢越M合出滿足需求的所有條件查詢語法,where
和whereOr
方法可以在一次查詢操作中多次調(diào)用,并且在調(diào)用Query
類的find
/select
/update
/delete
方法(及其衍生方法)的時候,由Builder
類最終生成一個包含查詢條件的SQL語句。
下面是一個測試查詢代碼:
Db::table('data')
->where('name', 'like', '%thinkphp')
->whereOr('id', '>', 1)
->find();
和下面的查詢結(jié)果可以自行比較下,然后揣測下where
和whereOr
方法的區(qū)別以及調(diào)用順序的影響:
Db::table('data')
->where('id', '>', 1)
->whereOr('name', 'like', '%thinkphp')
->find();
3個用法
3個用法就是表達(dá)式、數(shù)組和閉包用法,并且是支持混合使用的。
要掌握查詢語言的用法,必須在上面兩個方法和兩個用法的基礎(chǔ)上多嘗試,并且及時在頁面Trace中查看最終生成的SQL語句是否是滿足實際查詢需求(或者說是期望的查詢條件),這才是掌握查詢的不二法門。
先來說表達(dá)式用法,這是查詢語言的基礎(chǔ),查詢方法(包括where
和whereOr
,這里以where
為例)參數(shù)用法為:
where('字段名','表達(dá)式','查詢條件')
[info]#### where('字段名','查詢條件(等于)')
[info]#### where('字符串查詢條件','參數(shù)綁定(數(shù)組)')
表達(dá)式不分大小寫,支持的查詢表達(dá)式有下面幾種,分別表示的含義是:
表達(dá)式 | 含義 | 查詢條件類型 |
---|---|---|
= |
等于 | 字符串或者數(shù)字 |
<> |
不等于 | 字符串或者數(shù)字 |
> |
大于 | 字符串或者數(shù)字 |
>= |
大于等于 | 字符串或者數(shù)字 |
< |
小于 | 字符串或者數(shù)字 |
<= |
小于等于 | 字符串或者數(shù)字 |
[not] like |
模糊查詢 | 字符串 |
[not] between |
(不在)區(qū)間查詢 | 字符串或者數(shù)組 |
[not] in |
(不在)IN 查詢 | 字符串、數(shù)組或閉包 |
[not] null |
查詢字段是否(不)是NULL | 空 |
[not] exists |
EXISTS查詢 | 字符串或者閉包 |
exp |
表達(dá)式查詢,支持SQL語法 | 字符串 |
> time |
時間比較 | 字符串或者數(shù)字 |
< time |
時間比較 | 字符串或者數(shù)字 |
>= time |
時間比較 | 字符串或者數(shù)字 |
<= time |
時間比較 | 字符串或者數(shù)字 |
between time |
時間比較 | 字符串或者數(shù)字 |
notbetween time |
時間比較 | 字符串或者數(shù)字 |
測試代碼如下:
Db::table('data')
->where('id', '<>', 8)
->where('id', 'between', [1, 20])
->whereOr('name', 'like', '%thinkphp')
->select();
關(guān)于表達(dá)式查詢語法這里補(bǔ)充說明下,下面的幾種都是等效的:
表達(dá)式用法一 | 表達(dá)式用法二 |
---|---|
not like |
notlike |
not null |
notnull |
not in |
notin |
not between |
notbetween |
not exists |
notexists |
not between time |
notbetween time |
所有帶
between
的表達(dá)式,查詢條件必須包含兩個元素(或者用逗號分隔的兩個元素),否則會拋出異常,所以在傳入查詢條件之前最好先驗證下。
數(shù)組用法其實是多字段的表達(dá)式用法,在一個方法完成所有的查詢條件,用法如下:
where([
'字段名1' => ['表達(dá)式', '查詢條件'],
'字段名2' => ['表達(dá)式', '查詢條件'],
'字段名2' => '條件(等于)',
...
])
測試代碼:
Db::table('data')
->where(['name' => ['like', '%thinkphp'], 'id' => ['>', 1]])
->whereOr(['id' => ['<', 10], 'name' => ['like', '%php%']])
->find();
5.1須知
如果是5.1版本,數(shù)組查詢條件必須改成如下用法:
Db::table('data') ->where([ ['name', 'like', '%thinkphp'], ['id', '>', 1] ]) ->whereOr([ ['id', '<', 10], ['name', 'like', '%php%'] ]) ->find();
數(shù)組用法不夠靈活,有時候需要和其它用法配合使用,出于安全考慮也并不推薦。
閉包用法指的是直接在where
或者whereOr
方法中傳入閉包,和前面兩種用法配合可以完成復(fù)雜的查詢條件,例如:
$result = Db::table('data')
->where(function ($query) {
$query->where('id', 1)->whereOr('id', '>', 2);
})
->whereOr(function ($query) {
$query->where('name', 'like', '%think%')->where('id', '<>', 8);
})
->select();
閉包方法只有一個查詢對象參數(shù),如果需要在閉包中使用外部的變量,可以使用閉包的use
語法,例如:
$id = 1;
$name = 'think';
$result = Db::table('data')
->where(function ($query) use ($id) {
$query->where('id', $id);
})
->whereOr(function ($query) use ($id, $name) {
$query->where('name', 'like', '%' . $name . '%')->where('id', '<>', $id);
})
->select();
8個要訣:
在使用查詢的過程中謹(jǐn)記下面8
個要訣,幫助你更好的完成查詢。
- 查詢條件的調(diào)用次序就是生成SQL的條件順序;
- 查詢字段用
&
分割表示對多個字段使用AND
查詢; - 查詢字段用
|
分割表示對多個字段使用OR
查詢; - 對同一個查詢字段多次調(diào)用非等查詢條件會合并查詢;
- 閉包查詢和EXP查詢會在生成的查詢語句兩邊加上括號;
- 用閉包查詢替代3.2版本的組合查詢;
- 除了EXP查詢外,其它查詢都會自動使用參數(shù)綁定;
- 如果查詢條件來自用戶輸入,盡量使用表達(dá)式和閉包查詢,數(shù)組條件查詢務(wù)必使用官方推薦的方法獲取變量;
總結(jié)
本章我們學(xué)習(xí)了數(shù)據(jù)庫的基礎(chǔ)查詢用法,以及如何使用鏈?zhǔn)椒椒ㄍ瓿刹樵儤?gòu)造器。下一章我們會來學(xué)習(xí)下更多的高級查詢用法。