### 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