当前位置: 首页 > news >正文

ThinkPHP8学习篇(七):数据库(三)

在数据库操作环节,基础增删改查与简单条件查询已无法满足复杂业务需求,而聚合统计、分页展示、时间筛选、高级条件组合、子查询嵌套及原生SQL执行等能力,才是应对实际开发中多样数据处理场景的关键。本篇作为数据库系列文章的第三篇,学习核心内容将集中在聚合查询、分页查询的实现与参数配置、时间查询的常用方式、高级查询、子查询的构建与使用,以及原生查询的调用上。本篇文章将记录 ThinkPHP 数据库进阶查询能力的学习过程。

一、聚合查询

二、分页查询

1、简洁分页

2、分页参数

3、大数据分页

三、时间查询

1、时间比较

2、查询某个时间区间

3、查询某年/某月/某周/某天

4、时间字段区间比较

四、高级查询

1、快捷查询

2、批量(字段)查询

3、字符串条件查询

4、快捷方法

5、动态查询

6、条件查询

五、子查询

1、使用 fetchSql 方法

2、使用 buildSql 构造子查询

3、使用闭包构造子查询

六、原生查询

1、query 方法

2、execute 方法


一、聚合查询

在应用中我们经常会用到一些统计数据,例如当前所有(或者满足某些条件)的用户数、所有用户的最大积分、用户的平均成绩等等,ThinkPHP为这些统计操作提供了一系列的内置方法,包括:

方法

说明

count

统计数量,参数是要统计的字段名(可选)

max

获取最大值,参数是要统计的字段名(必须)

min

获取最小值,参数是要统计的字段名(必须)

avg

获取平均值,参数是要统计的字段名(必须)

sum

获取总分,参数是要统计的字段名(必须)

聚合方法如果没有数据,默认都是0,聚合查询都可以配合其它查询条件。

示例

// 1、获取 user 表数据总条数,使用 count 方法
Db::table('user')->count();
// 生成的SQL语句为:SELECT COUNT(*) AS think_count FROM `user`// 2、根据字段统计表中数据总量,使用 count 方法并传入字段
Db::table('user')->count('id');
// 生成的SQL语句为:SELECT COUNT(`id`) AS think_count FROM `user`// 3、获取 user 表中 age 字段的最大值,使用 max 方法
Db::table('user')->max('age');
// 生成的SQL语句为:SELECT MAX(`age`) AS think_max FROM `user`

在这里列举了 countmaxmin 三个方法的使用示例,其它方法使用与这三个方法是一样的,这里不再一一进行演示。

如果要使用 group 进行聚合查询,需要自己实现查询。例如:

Db::table('score')->field('user_id,SUM(score) AS sum_score')->group('user_id')->select();

二、分页查询

ThinkPHP 内置了分页实现,要给数据添加分页输出功能变得非常简单,可以直接在 Db 类查询的时候调用 paginate 方法。与之前使用的分页 page 方法不同,paginate 方法查询的内容可以作用在模板文件中,会自动的在模板中生成分页相关内容,可以进行分页操作。

示例

// 查询 user 表数据,根据 id 降序排序,每页显示 1 条数据(方便分页展示)
$list = Db::name('user')->order('id', 'desc')->paginate(1);// 渲染模板输出
return view('index', ['list' => $list]);

模板文件中分页输出代码如下:

<div><ul>{volist name='list' id='user'}<li> {$user.name}</li>{/volist}</ul>
</div>
<!-- 添加此行代码,将在页面中生成分页信息 -->
{$list|raw}

效果:

通过执行后的效果可以看到,页面中生成了分页信息的相关展示,此时点击页码可以跳转到页码指定页,后台不需要做任何的更改。

通过这个示例可以看到,paginate 方法进行分页十分简单,不需要像以往的 web 开发那样,需要在后台代码中获取当前页码等操作。

默认情况下,生成的分页输出是完整分页功能,带总分页数据和上下页码,分页样式只需要通过样式修改即可,完整分页默认生成的分页输出代码为:

<!-- 默认生成的分页代码 -->
<ul class="pagination"><li><a href="?page=1">&laquo;</a></li><li><a href="?page=1">1</a></li><li class="active"><span>2</span></li><li class="disabled"><span>&raquo;</span></li>
</ul>

如果需要获取数据总量,即一共多少条数据,可以这样做:

$list = Db::name('user')->order('id', 'desc')->paginate(1);
// 获取总记录数
$count = $list->total();
// 此时总记录数 count 渲染到模板,可以在模板中对其进行展示
return view('index', ['list' => $list, 'count' => $count]);

paginate 方法也支持传入总记录数。当传入总记录数后,paginate 方法将不会自动进行总数计算。例如:

// 总记录数为 50,paginate(每页显示数据条数, 数据总记录数)
$list = Db::name('user')->order('id', 'desc')->paginate(1, 50);
return view('index', ['list' => $list]);

官网建议:对于 UNION 查询以及一些特殊的复杂查询,推荐使用这种方式首先单独查询总记录数,然后再传入分页方法。

支持分页类后数据直接 each 遍历处理,方便修改分页后的数据。

// 将 name 字段的值更改为大写
$list = Db::name('user')->order('id', 'desc')->paginate(1)->each(function($item, $key) {$item['name'] = strtoupper($item['name']);return $item;
});

1、简洁分页

如果不想输出全部的分页页码,仅仅只有上下页的分页展示,可以使用下面的简洁分页代码:

// paginate 方法第二个参数传入 ture 表示使用简洁分页
$list = Db::name('user')->order('id', 'desc')->paginate(1, true);
return view('index', ['list' => $list]);

简洁分页模式的输出代码为:

<ul class="pager"><li><a href="?page=1">&laquo;</a></li><li class="disabled"><span>&raquo;</span></li>
</ul>

页面显示效果:

由于简洁分页模式不需要查询总数据数,因此可以提高查询性能。

2、分页参数

主要的分页参数如下表所示:

参数

描述

list_rows

每页数量

page

当前页

path

url路径

query

url额外参数

fragment

url锚点

var_page

分页变量

分页参数的设置可以在调用分页方法的时候传入,例如:

$list = Db::name('user')->paginate(['list_rows'=> 20,'var_page' => 'page',
]);

如果需要在分页的时候传入查询条件,可以使用 query 参数拼接额外的查询参数。

3、大数据分页

对于大数据量的分页查询,系统提供了一个高性能的 paginateX 分页查询方法,用法和 paginate 分页查询存在一定区别。如果要分页查询的数据量在百万级以上,使用 paginateX 方法会有明显的提升,尤其是在分页数较大的情况下。并且由于针对大数据量而设计,该分页查询只能采用简洁分页模式,所以没有总数。

分页查询的排序字段一定要使用索引字段,并且是连续的整型,否则会有数据遗漏。

主要场景是针对主键进行分页查询,默认使用主键倒序查询分页数据。

$list = Db::name('user')->paginateX(1);

也可以在查询的时候可以指定主键和排序。

// paginateX(每页显示数据条数, 主键, 排序方式)
$list = Db::name('user')->paginateX(1, 'id', 'asc');

查询方法会执行两次查询,第一次查询用于查找满足当前查询条件的最大或者最小值,然后配合主键查询条件来进行分页数据查询。

三、时间查询

1、时间比较

框架内置了常用的时间查询方法,并且可以自动识别时间字段的类型,所以无论采用什么类型的时间字段,都可以统一使用时间查询方法。

whereTime 方法提供了日期和时间字段的快捷查询。

示例

// 大于某个时间
Db::name('user')->whereTime('birthday', '>=', '1970-10-1')->select();
// 时间区间查询
Db::name('user')->whereTime('birthday', 'between', ['1970-10-1', '2000-10-1'])->select();

还可以使用下面的时间表达式进行时间查询:

// 查询两个小时内的注册的用户
Db::name('user')->whereTime('create_time','-2 hours')->select();

2、查询某个时间区间

针对时间的区间查询,系统提供了 whereBetweenTime / whereNotBetweenTime 快捷方法。

示例

// 查询2025年上半年注册的用户
Db::name('user')->whereBetweenTime('create_time', '2025-01-01', '2025-06-30')->select();
// 查询不是2025年上半年注册的用户
Db::name('user')->whereNotBetweenTime('create_time', '2025-01-01', '2025-06-30')->select();

3、查询某年/某月/某周/某天

// 查询今年注册的用户
Db::name('user')->whereYear('create_time')->select();
// 查询去年注册的用户
Db::name('user')->whereYear('create_time', 'last year')->select();
// 查询某一年的数据
Db::name('user')->whereYear('create_time', '2018') // 传入具体年份->select();

类似 whereYear 的方法还有:whereMonth 方法查询某月,whereWeek 方法查询某周,whereDay 方法查询某天。

4、时间字段区间比较

whereBetweenTimeField 方法可以支持对两个时间字段的区间比较。

示例

Db::name('event')->whereBetweenTimeField('start_time', 'end_time')->select();
// 上面的查询相当于
Db::name('event')->whereTime('start_time', '<=', time())->whereTime('end_time', '>=', time())->select();

四、高级查询

1、快捷查询

快捷查询方式是一种多字段相同查询条件的简化写法,可以进一步简化查询条件的写法,在多个字段之间用 | 分割表示 OR 查询,用 & 分割表示 AND 查询。

示例

Db::table('user')->where('name|title','like','thinkphp%')->where('create_time&update_time','>',0)->select();
// 生成的SQL语句为:SELECT * FROM `user` WHERE ( `name` LIKE 'thinkphp%' OR `title` LIKE 'thinkphp%' ) AND ( `create_time` > 0 AND `update_time` > 0 )

快捷查询支持所有的查询表达式。

2、批量(字段)查询

可以进行多个条件的批量条件查询定义,例如:

Db::table('user')->where([['name', 'like', 'thinkphp%'],['title', 'like', '%thinkphp'],['id', '>', 0],['status', '=', 1],])->select();
// 生成的SQL语句为:SELECT * FROM `user` WHERE `name` LIKE 'thinkphp%' AND `title` LIKE '%thinkphp' AND `id` > 0 AND `status` = '1'

数组方式如果使用 exp 查询的话,一定要用 raw 方法。

示例

Db::table('user')->where([['name', 'like', 'thinkphp%'],['title', 'like', '%thinkphp'],['id', 'exp', Db::raw('>score')],['status', '=', 1],])->select();

如果希望某一个 where 方法里面的条件单独处理,可以使用下面的方式,避免被其它条件影响。

$map = [['name', 'like', 'thinkphp%'],['title', 'like', '%thinkphp'],['id', '>', 0],];
Db::table('user')->where([ $map ])->where('status',1)->select();
// 生成的SQL语句为:SELECT * FROM `user` WHERE ( `name` LIKE 'thinkphp%' AND `title` LIKE '%thinkphp' AND `id` > 0 ) AND `status` = '1'
// 从生成的SQL语句中可以看到,map 定义的条件被 () 包起来了

也可以像下面这样使用多个条件组合:

$map1 = [['name', 'like', 'thinkphp%'],['title', 'like', '%thinkphp'],];$map2 = [['name', 'like', 'kancloud%'],['title', 'like', '%kancloud'],];    Db::table('user')->whereOr([ $map1, $map2 ])->select();
// 生成的SQL语句为:SELECT * FROM `user` WHERE ( `name` LIKE 'thinkphp%' AND `title` LIKE '%thinkphp' ) OR ( `name` LIKE 'kancloud%' AND `title` LIKE '%kancloud' )
// 从生成的SQL语句中可以看到,每个被定义的条件都被 () 抱起来了

善用多维数组查询,可以很方便的拼装出各种复杂的SQL语句。

3、字符串条件查询

对于一些复杂的查询,也可以直接使用原生SQL语句进行查询。

示例

Db::table('user')->whereRaw('id > 0 AND name LIKE "thinkphp%"')->select();

为了安全起见,我们可以对字符串查询条件使用参数绑定,例如:

Db::table('user')->whereRaw('id > :id AND name LIKE :name ', ['id' => 0, 'name' => 'thinkphp%'])->select();

4、快捷方法

系统封装了一系列快捷方法,用于简化查询,包括:

方法

作用

whereOr

字段OR查询

whereXor

字段XOR查询

whereNull

查询字段是否为Null

whereNotNull

查询字段是否不为Null

whereIn

字段IN查询

whereNotIn

字段NOT IN查询

whereBetween

字段BETWEEN查询

whereNotBetween

字段NOT BETWEEN查询

whereLike

字段LIKE查询

whereNotLike

字段NOT LIKE查询

whereExists

EXISTS条件查询

whereNotExists

NOT EXISTS条件查询

whereExp

表达式查询

whereColumn

比较两个字段

示例 whereColumn 方法:两个字段比较的查询条件

// 查询 update_time 大于 create_time 的用户数据
Db::table('user')->whereColumn('update_time','>','create_time')->select();
// 生成的SQL语句为:SELECT * FROM `user` WHERE ( `update_time` > `create_time` ) // 支持数组方式比较多个字段
Db::name('user')->whereColumn([['title', '=', 'name'],['update_time', '>=', 'create_time'],
])->select();
// SELECT * FROM `user` WHERE ( `name` = `nickname` AND `update_time` > `create_time` ) 

5、动态查询

查询构造器还提供了动态查询机制,用于简化查询条件,包括:

动态查询

描述

whereFieldName

查询某个字段的值

whereOrFieldName

查询某个字段的值

getByFieldName

根据某个字段查询

getFieldByFieldName

根据某个字段获取某个值

其中 FieldName 表示数据表的实际字段名称的驼峰法表示。

假设数据表 user 中有 emailnick_name 字段,我们可以这样来查询。

// 根据邮箱(email)查询用户信息
$user = Db::table('user')->whereEmail('thinkphp@qq.com') // whereEmail 中的 Email 就是数据表 user 中的 email 字段的驼峰表示法->find();// 根据昵称(nick_name)查询用户
$email = Db::table('user')->whereNickName('like', '%流年%') // whereNickName 中的 NickName 就是数据表 user 中的 nick_name 字段的驼峰表示法->select();

其它方法的使用同示例所示方法是一样的。

6、条件查询

查询构造器支持条件查询。

示例

Db::name('user')->when($condition, function ($query) {// 满足 $condition 条件后执行,否则不执行$query->where('score', '>', 80)->limit(10);
})->select();
// 执行说明:
// 1、$condition 为 True,生成的SQL语句为:SELECT * FROM `user` WHERE  `score` > 80 LIMIT 10
// 2、$condition 为 False,生成的SQL语句为:SELECT * FROM `user`

并且支持不满足条件的分支查询:

Db::name('user')->when($condition, function ($query) {// 满足 $condition 条件后执行$query->where('score', '>', 80)->limit(10);
}, function ($query) {// 不满足 $condition 条件执行$query->where('score', '>', 60);
});

五、子查询

首先构造子查询SQL,可以使用以下三种方式来构建子查询。

1、使用 fetchSql 方法

fetchSql 方法表示不进行查询而只是返回构建的SQL语句,并且不仅仅支持 select,而是支持所有的CURD查询。

示例

$subQuery = Db::table('user')->field('id,name')->where('id', '>', 10)->fetchSql(true)->select();
// 生成的subQuery结果为:SELECT `id`,`name` FROM `user` WHERE `id` > 10

2、使用 buildSql 构造子查询

$subQuery = Db::table('user')->field('id,name')->where('id', '>', 10)->buildSql();
// 生成的subQuery结果为:( SELECT `id`,`name` FROM `user` WHERE `id` > 10 )

调用 buildSql 方法后不会进行实际的查询操作,而只是生成该次查询的SQL语句(为了避免混淆,会在SQL两边加上括号),然后我们直接在后续的查询中调用。

然后使用子查询构造新的查询:

Db::table($subQuery . ' a')->where('a.name', 'like', 'zhangsan')->order('id', 'desc')->select();
// 生成的SQL语句为:SELECT * FROM ( SELECT `id`,`name` FROM `user` WHERE  `id` > 10 ) a WHERE  `a`.`name` LIKE 'zhangsan' ORDER BY `id` DESC

3、使用闭包构造子查询

IN/NOT INEXISTS/NOT EXISTS 之类的查询可以直接使用闭包作为子查询。

示例

Db::table('user')->where('id', 'IN', function ($query) {$query->table('role')->where('status', 1)->field('id');
})
->select();
// 生成的SQL语句为:SELECT * FROM `user` WHERE  `id` IN (SELECT `id` FROM `role` WHERE  `status` = 1)

除了上述查询条件外,比较运算也支持使用闭包子查询。

六、原生查询

Db 类支持原生SQL查询操作,主要包括 queryexecute 两个方法。

1、query 方法

query 方法用于执行SQL查询操作,返回查询结果数据集(数组)。

示例

Db::query('select * from user where id=:id', ['id' => 1])

2、execute 方法

execute 用于更新和写入数据的sql操作,支持在原生查询的时候使用参数绑定,包括问号占位符或者命名占位符。如果数据非法或者查询错误则返回 false,否则返回影响的记录数。

示例

// 问号占位符
Db::execute('update user set name=? where id=?', ['wangwu', 1]);
// 命名占位符
Db::execute('update user set name=:name where id=:id', ['name' => 'zhangsan', 'id' => 1]);

注意:不支持对表名使用参数绑定。

http://www.dtcms.com/a/394323.html

相关文章:

  • 链家租房数据爬虫与可视化项目 Python Scrapy+Django+Vue 租房数据分析可视化 机器学习 预测算法 聚类算法✅
  • MQTT协议知识点总结
  • C++ 类和对象·其一
  • TypeScript里的类型声明文件
  • 【LeetCode - 每日1题】设计电影租借系统
  • Java进阶教程,全面剖析Java多线程编程,线程安全,笔记12
  • DCC-GARCH模型与代码实现
  • 实验3掌握 Java 如何使用修饰符,方法中参数的传递,类的继承性以及类的多态性
  • 【本地持久化】功能-总结
  • 深入浅出现代FPU浮点乘法器设计
  • LinkedHashMap 访问顺序模式
  • 破解K个最近点问题的深度思考与通用解法
  • 链式结构的特性
  • 报表1-创建sql函数get_children_all
  • 9月20日 周六 农历七月廿九 哪些属相需要谨慎与调整?
  • godot实现tileMap地图
  • 【Unity+VSCode】NuGet包导入
  • QEMU虚拟机设置网卡模式为桥接,用xshell远程连接
  • Week 17: 深度学习补遗:Boosting和量子逻辑门
  • 【论文速递】2025年第13周(Mar-23-29)(Robotics/Embodied AI/LLM)
  • Webpack进阶配置
  • 【LeetCode 每日一题】3227. 字符串元音游戏
  • 【图像算法 - 26】使用 YOLOv12 实现路面坑洞智能识别:构建更安全的智慧交通系统
  • 009 Rust函数
  • IT疑难杂症诊疗室
  • 视频播放器下载推荐,PotPlayer‌,KMPlayer,MPC-HC,GOM Player‌VLC media player,MPV,
  • Day04 分治 递归 | 50. Pow(x, n)、22. 括号生成
  • (博主大回归)洛谷题目:P1986 元旦晚会 题解 (本题简)
  • Windows Docker 环境下 VLLM 大模型存储最优解:Docker-Desktop 实例目录与多容器协同挂载方案
  • Elasticsearch面试精讲 Day 20:集群监控与性能评估