### 2018 年 11 月 22 日 發布
>[danger] 今天是感恩節,首先感謝所有關注和使用`ThinkPHP`的用戶,尤其要對那些為框架做過貢獻的用戶說聲感恩,因為有你們的大力支持,`ThinkPHP`才能堅持到今天還在不斷更新完善,我也還能寫下這篇博客。
本文給大家不完全的整理了一些能夠提高開發效率的查詢技巧,希望能給你的開發工作帶來更多的方便,不過也許有些技巧可能你已經了然于胸了,就當鞏固學習了。
>[info] 本文中所有的查詢示例都以模型用法為例,只為為了進一步說明所有的查詢構造器用法都適用于模型。
## 查詢值為Null的數據
```
// 查詢email為空,并且name不為空的用戶數據
User::whereNull('email')
->whereNotNull('name')
->select();
```
## 多個字段同一個查詢條件
快捷查詢方式是一種多字段相同查詢條件的簡化寫法,可以進一步簡化查詢條件的寫法,在多個字段之間用`|`分割表示`OR`查詢,用`&`分割表示`AND`查詢,例如:
```
User::where('name|title','like','thinkphp%')
->where('create_time&update_time','>',0)
->find();
```
## 數組對象查詢
如果你升級老版本的系統到`5.1`,由于數組查詢方式的變化,而你又不希望全部換成表達式查詢,那么可以使用數組對象查詢。
```
use think\db\Where;
// 5.0的數組查詢條件
$map = [
'name' => ['like', 'thinkphp%'],
'title' => ['like', '%think%'],
'id' => ['>', 10],
'status' => 1,
];
User::where(new Where($map))
->select();
```
只需要把原來的
```
where($map)
```
改成
```
where(new Where($map))
```
即可完成簡單的數組查詢升級兼容。
## 使用快捷方法
對于一些常用的查詢,系統封裝了快捷查詢方法,例如:
```
User::whereIn('id', [1,2,3])
->whereLike('name', 'think%')
->select();
```
相當于下面的查詢
```
User::where('id', 'in', [1,2,3])
->where('name', 'like', 'think%')
->select();
```
更多的方法可以參考官方手冊或者使用IDE的自動提示。
## 獲取字段值和列數據
對于一些簡單的數據獲取,你完全不需要查詢整個表的數據,例如查詢某個字段(滿足條件的)值或者列數據。
```
// 獲取id為10的用戶名稱
User::where('id', 10)
->value('name');
// 獲取狀態為1的用戶名稱列表
User::where('status', 1)
->column('name');
// 獲取分數大于80的用戶分數列表,以用戶ID為索引
User::where('score', '>', 80)
->column('score', 'id');
```
## 聚合查詢
可以直接進行各種聚合查詢,包括:
```
User::count();
User::max('score');
User::min('score');
User::avg('score');
Blog::sum('read_count');
```
如果你的`min`/`max`查詢的是一個字符串類型字段,記得加上第二個參數。
```
// 獲取name字段的最大值
User::max('name', false);
```
## 時間區間查詢
時間查詢主要用于時間字段的區間查詢,支持所有類型的時間字段。
```
// 大于某個時間
User::whereTime('birthday', '>=', '2008-10-1')
->select();
// 小于某個時間
User::whereTime('birthday', '<', '2000-10-1')
->select();
// 時間區間查詢
User::whereBetweenTime('birthday', '1990-10-1', '2000-10-1')
->select();
// 不在某個時間區間
User::whereNotBetweenTime('birthday', '1970-10-1', '2000-10-1')
->select();
```
如果`whereBetweenTime`方法沒有指定第三個參數,則表示查詢當天的數據
```
// 查詢2000年10月1日出生的用戶
User::whereBetweenTime('birthday', '2000-10-1')
->select();
```
## 時間表達式查詢
對于一些非具體的時間查詢,比較適合使用時間表達式進行查詢,例如:
```
// 獲取今天的博客
Blog::whereTime('create_time', 'today')
->select();
// 獲取昨天的博客
Blog::whereTime('create_time', 'yesterday')
->select();
// 獲取本周的博客
Blog::whereTime('create_time', 'week')
->select();
// 獲取上周的博客
Blog::whereTime('create_time', 'last week')
->select();
// 獲取本月的博客
Blog::whereTime('create_time', 'month')
->select();
// 獲取上月的博客
Blog::whereTime('create_time', 'last month')
->select();
// 獲取今年的博客
Blog::whereTime('create_time', 'year')
->select();
// 獲取去年的博客
Blog::whereTime('create_time', 'last year')
->select();
```
高級的時間表達式查詢可以使用PHP的[相對時間格式](http://php.net/manual/zh/datetime.formats.relative.php),例如:
```
// 查詢兩天以內的博客
Blog::whereTime('create_time','-2 days')
->select();
// 查詢昨天中午后發的博客
Blog::whereTime('create_time','yesterday noon')
->select();
```
更多的時間表達式查詢你可以自由發揮。
## 時間字段范圍查詢
你可以查詢當前時間是否在兩個時間字段區間范圍內,通常用于一些活動以及優惠券的有效期查詢等等。
```
// 查詢有效期內的活動
Event::whereBetweenTimeField('start_time','end_time')
->select();
// 查詢沒有開始或者已經過期的活動
Event::whereNotBetweenTimeField('start_time','end_time')
->select();
```
## 字段比較
可以直接比較兩個字段的大小進行查詢
```
User::whereColumn('update_time', '>', 'create_time')
->select();
User::whereColumn('score1', '>', 'score2')
->select();
```
如果需要比較兩個字段相同,可以使用
```
User::whereColumn('score1', 'score2')
->select();
```
## 動態查詢
使用動態查詢可以進一步簡化你的查詢條件,不過缺點是可能無法做到IDE的自動提示了,例如:
```
// 根據郵箱(email)查詢用戶信息
User::whereEmail('thinkphp@qq.com')
->find();
// 根據昵稱(nick_name)查詢用戶
User::whereNickName('like', '%流年%')
->select();
// 根據郵箱查詢用戶信息
User::getByEmail('thinkphp@qq.com');
// 根據昵稱(nick_name)查詢用戶信息
User::getByNickName('流年');
// 根據郵箱查詢用戶的昵稱
User::getFieldByEmail('thinkphp@qq.com', 'nick_name');
// 根據昵稱(nick_name)查詢用戶郵箱
User::getFieldByNickName('流年', 'email');
```
## 條件查詢
利用條件查詢你可以很方便的控制查詢條件分支,你再也不需要在組裝查詢條件的時候寫大量的`if`和`else`了。
```
User::when($condition, function ($query) {
// 滿足條件后執行
$query->where('score', '>', 80)->limit(10);
})->select();
```
并且支持不滿足條件的分支查詢,并且支持多次調用`when`方法。
```
User::when($condition, function ($query) {
// 滿足條件后執行
$query->where('score', '>', 80)->limit(10);
}, function ($query) {
// 不滿足條件執行
$query->where('score', '>', 60);
});
```
## JSON查詢
如果你的字段類型使用的是JSON類型,那么可以直接使用框架提供的JSON查詢支持。
```
User::where('info->nickname', 'ThinkPHP')
->find();
```
注意,需要在模型里面定義`JSON`字段屬性。
```
<?php
namespace app\index\model;
use think\Model;
class User extends Model
{
// 設置json類型字段
protected $json = ['info'];
}
```
如果使用Db查詢的話,可以改為
```
$user = Db::name('user')
->json(['info'])
->where('info->nickname','ThinkPHP')
->find();
```
## SQL函數查詢
如果需要對某個字段使用SQL函數表達式查詢,可以使用
```
User::whereExp('nickname', "= CONCAT(name, '-', id)")
->whereRaw('LEFT(nickname, 5) = ?', ['think'])
->select();
```
注意`whereExp`和`whereRaw`方法的區別,前者是對某個字段使用SQL函數表達式,后者是整個查詢就是一個SQL函數表達式。
## 字段遞增/遞減
單獨對某個字段進行遞增/遞減操作,可以用:
```
// score 字段加 1
User::where('id', 1)
->setInc('score');
// score 字段加 5
User::where('id', 1)
->setInc('score', 5);
// score 字段減 1
User::where('id', 1)
->setDec('score');
// score 字段減 5
User::where('id', 1)
->setDec('score', 5);
```
可以支持延時更新
```
// 延時30秒更新score字段
User::where('id', 1)->setInc('score', 1, 30);
```
如果需要同時遞增/遞減多個字段的話,可以使用:
```
// 博客的閱讀數遞增1 評論數遞減2
Blog::where('id', 10)
->inc('read_count')
->dec('comment_count', 2)
->update();
```
## 指定字段值排序
如果你需要按照指定字段的值的順序來排序,可以使用
```
User::where('status', 1)
->orderField('id', [1,2,3])
->select();
```
## 從主庫讀取
如果你使用了數據庫的主從分離,當剛寫入數據后,數據庫的主從同步可能還沒來得及同步,這個時候立刻查詢數據可能會出錯,你可以使用下面的方法從主庫讀取。
```
$user = User::create($data);
$user->readMaster()->select();
```
你可以全局配置數據寫入后自動讀取主庫
```
// 模型寫入后自動讀取主服務器
'read_master' => true,
```
## 獲取自增ID
使用`Db`類的`insert`方法或者模型的`save`方法返回的不是自增主鍵,不過你可以使用。
```
$userId = Db::name('user')->insertGetId($data);
```
如果使用模型的話,自增主鍵的值會自動賦值給模型對象,可以直接獲取。
```
$user = User::create($data);
echo $user->id;
```
## 模型查詢為空的處理
模型查詢數據不存在的話返回值為Null,所以必須要添加返回值判斷然后進行數據處理,如果你不想手動判斷,可以使用下面的方法查詢,如果數據不存在則返回空的模型對象。
```
// 始終返回模型對象
$user = User::where('id', 10)->findOrEmpty();
```
## 自動分批寫入
如果你需要一次寫入大量數據,建議使用`limit`方法自動分批多次寫入。
```
// 自動分批多次寫入數據庫 每次最多寫入1000條
Db::name('user')
->limit(1000)
->insertAll($dataList);
```
如果是使用模型的話,建議直接使用`saveAll`方法而不需要`limit`方法。
```
$user = new User;
$user->saveAll($dataList);
```
## 數據分批處理
對于大量數據的處理操作,可以使用`chunk`分批處理方法。
```
// 每次處理100個數據
User::chunk(100, function($users) {
foreach ($users as $user) {
// 處理數據
}
});
```
## 游標查詢
對于內存開銷比較大的應用,在做大量數據查詢和處理的時候,使用`cursor`方法進行游標查詢,可以利用PHP的生成器特性,減少內存占用。
```
$cursor = User::cursor();
foreach($cursor as $user){
// 處理數據
}
```
## 總結
善于運用查詢構造器封裝的快捷方法,可以大大提高開發效率,讓你更專注于業務邏輯,而不是怎么寫查詢代碼。
另外模型的很多功能都是為了提高開發效率而設計的,這里涉及太多的技巧,就不再一一描述了,以后專門來講吧。
- 值得升級到5.1的18個理由
- 5.1.7版本新特性
- JSON字段類型在ORM中的使用
- 文件下載響應對象
- 教你使用5.1的數組對象查詢
- 模型三大利器之一:搜索器
- 在ThinkPHP中使用Yaconf
- 掌握命令行的表格輸出
- 5.1.25查詢參數綁定的改進
- ThinkPHP安全規范指引
- 巧用數據集的排序功能實現統計排序
- think-orm ——基于5.1的獨立ORM庫
- think-template——基于ThinkPHP的獨立模板引擎
- ThinkPHP5.1.26版本發布——修正版本,包含安全更新
- ThinkPHP5.0和3.2再發安全更新
- 官宣:ThinkPHP發布首個LTS版本
- 你真的了解Db類和模型的正確使用姿勢么?
- 如何更有效的記錄和管理日志
- 模型三大利器之二:修改器
- ThinkPHP5.1.28版本發布——修正上一版本問題,改進關聯查詢
- 模型三大利器之三:獲取器
- API版本控制的幾種思路
- ThinkPHP5.2第一個Beta版本發布測試
- 讓你少犯錯的數據查詢基本原則
- ThinkPHP發布5.1.29版本——常規更新
- 這15個好習慣讓你更容易升級到5.2
- 如何有效提高ThinkPHP的應用性能
- 讓你提高開發效率的查詢技巧
- 模型關聯查詢不完全指南
- 5.2發布Beta2版本——統一和精簡大量用法
- ThinkPHP發布5.1.30版本——支持微秒時間字段寫入
- ThinkPHP的數據緩存使用
- ThinkPHP5.2安裝及入口文件
- ThinkPHP榮獲2018 年度最受歡迎中國開源開發框架第1名
- 5.1路由使用心得技巧
- ThinkPHP5.*版本發布安全更新
- ThinkPHP項目及代碼規范指北
- 5.2版本的設計規范指導
- ThinkPHP5.1.32版本發布——圣誕快樂
- 利用Trait特性給模型增加樂觀鎖功能
- 5.2數據庫和模型的變化(摘要)
- ThinkPHP模板引擎實現和常見問題
- ThinkPHP5.0.24版本發布——安全更新
- 不忘初心,方得始終——ThinkPHP十三周年報告
- ThinkPHP5+相關資源匯總
- 異步社區ThinkPHP周年慶專享優惠活動
- 5.2路由的調整和改進
- ThinkPHP發布5.1.33版本——包含安全更新
- ThinkPHP擴展開發指南
- ThinkPHP發布5.2Beta3版本
- ThinkPHP發布5.1.34版本——喜迎新年
- ThinkPHP發布5.2RC1版本
- ThinkPHP發布5.1.35版本——常規更新
- 5.2配置類的調整
- 5.2時間查詢的改進和優化
- 5.2RC版本升級不完全指導(僅供學習參考)
- ThinkPHP5.2版本正式變更為6.0版本
- ThinkPHP百度云云虛擬主機專享免費活動
- 事件系統以及查詢事件、模型事件的使用
- ThinkPHP6.0RC2版本發布——架構升級、精簡核心
- ThinkPHP5.1.36LTS版本發布——常規更新
- 新版Session和Cookie設計變化
- ThinkPHP5.1.37版本發布——常規更新
- ThinkPHP6.0RC3版本發布——細節完善,體驗優化
- 6.0中間件使用詳解
- Composer各大廠商鏡像地址
- ThinkPHP6.0發布計劃公告
- 「ThinkPHP開發者周刊」招募志愿者
- ThinkPHP6.0日志變化
- ThinkPHP5.1.38版本發布——常規更新
- ThinkPHP6.0RC4版本發布——ORM獨立,日志多通道支持
- ThinkORM2.0開發指南上線
- ThinkPHP6.0RC5版本發布——多應用模式獨立,中間件機制調整
- ThinkPHP6.0版本發布——程序員節福利
- ThinkPHP5.1.39LTS版本發布——常規更新
- ThinkPHP6.0.1版本發布——圣誕快樂!
- 回顧2019,展望2020!
- ThinkPHPV6.0.2版本發布——2020新春快樂!
- 周年福利系列:Swoole合作優惠
- 億速云成為ThinkPHPV6.0獨家贊助發布商??
- 新冠疫情工具和限免資源專題(保持更新中)
- 周年福利系列:創宇信用認證合作優惠
- 周年福利系列:碼云企業版限時10%優惠
- 周年福利系列:想天短說抵現優惠
- think-swoole直播:從零開始掌握swoole開發
- 周年福利系列:B2C開源電商ShopXO授權8折優惠
- 周年福利系列:LayuiAdmin 永久授權限時優惠
- ThinkPHP資源導航站上線——構建生態 服務未來
- ThinkPHP官方技術支持服務和應用服務市場上線公測
- ThinkPHP市場精選——推廣基本要素
- ThinkPHP市場精選——客服聊天專題
- ThinkPHPV6.0.3版本發布——端午安康
- ThinkPHP開發者扶持計劃
- 6.0.3版本關鍵更新及升級事項
- 「ThinkPHP開發者周刊」改版重啟
- ThinkPHP市場精選——企業建站專題
- ThinkPHP 提供統一API接口服務
- ThinkPHP市場精選——直播電商專題
- ThinkAPI服務SDK發布
- 官方服務市場啟用獨立子域名
- ThinkPHP市場精選——刷臉支付專題
- ThinkAPI推出會員服務計劃
- ThinkPHPV6.0.4版本發布——中秋國慶雙節快樂
- ThinkPHPV5.1.40版本發布——常規更新
- 1024程序員節福利走一波
- ThinkPHP V6.0.5版本發布——兼容Composer2.0
- 知識圖譜應用場景——源論技術沙龍
- ThinkPHP5.*版本改進Composer2.0的兼容
- 官方市場雙十一精選推薦
- 技術人做產品有機會么(文末送課程)
- 本周秒殺——古德云售后獲客營銷系統
- ThinkAPI服務更新——支持接口分組和PHP版本依賴調整
- PHP8新特性盤點
- PHP8新特性系列:構造器屬性提升使用及注意事項
- ThinkPHP2021新年寄語
- ThinkPHP V6.0.6&V5.1.41版本發布——兼容PHP8.0
- PHP如何更優雅地調用API接口
- ThinkPHP V6.0.7發布——修正版本
- ThinkAPI服務更新——IP白名單
- 最新版ThinkORM對于時間字段的調整
- ThinkAPI短信接口正式上線
- ThinkPHP V6.0.8版本發布——多環境變量配置支持
- 頂想云寫作服務開啟第一次公測
- ThinkSSL上線——官方SSL/TLS證書服務
- MDBootstrap國內用戶福利——ThinkPHP官方市場首發
- ThinkPHP V6.0.9版本發布——常規更新
- ThinkORM功能盤點——虛擬模型
- 全面支持主流GIT版本庫——云寫作服務第二次公測
- 云寫作服務私有化部署方案之:版本庫私有化
- 看云雙十一活動
- ThinkPHP V6.0.10LTS發布——兼容PHP8.1
- ThinkPHP V6.0.12發布——命令行兼容8.1
- 頂想云知識管理上線公測——構建企業文檔中心和知識庫
- 頂想云上線——助力生態數字化建設
- 618活動進行中——官方市場迎來一波更新
- 頂想云知識管理正式上線——看云文檔啟動遷移服務
- ThinkPHP V6.0.13發布——常規更新
- 頂想云網站助理服務上線——構建產品支持服務
- ThinkPHP發布6.1.0&6.0.14版本——安全更新
- ThinkPHP新版社區上線試運營
- ThinkAPI上架人臉核身接口——助力網站實名認證
- 辭舊迎新——舊版社區停止注冊及發帖
- ThinkPHP6.1.2版本發布——兼容PHP8.2