数据查询Fetch
可使用的方法有:get(),first(),value(),column(),find(),chunk()
查询多条数据get() 或many()
get($type), 如果$type为true时,不执行查询,直接返回构建的sql语句
// 获取多条记录
Db::table('test')->get();
// 生成SQL: "SELECT * FROM test"
// 获取指定字段的多条记录
Db::select('title')->table('pages')->where('id', 17)->get();
// 生成SQL: "SELECT title FROM pages WHERE id='17'"
// 获取指定字段的多条记录
Db::select('username')->table('users')->where('status', 1)->get();
// 生成SQL: "SELECT username FROM users WHERE status='1'"获取单条数据 First()或 one() 别名
成功返回一维数据,失败返回null
$user = Db::table('users')->where('status', 1)->first();
// 结果:单个用户对象或数组获取单一字段 Value
成功返回字段值对应的数值,失败返回null, 这个方法可以从一条数据中获取到单一字段的值
// 获取单个字段的值
$username = Db::table('users')->where('id', 1)->value('username');
// 结果:字符串 "admin"按字段提取列数据column
获取单列数据:
// 获取所有用户名
$usernames = Db::table('users')->column('username');
// 结果: ['admin', 'user1', 'user2', ...]构建键值对:
// 获取用户ID到邮箱的映射
$emails = Db::table('users')->column('email', 'id');
// 结果: [1 => 'admin@example.com', 2 => 'user1@example.com', ...]用于选择框:
// 获取分类下拉框选项
$categories = Db::table('categories')->column('name', 'id');
// 在视图中使用
foreach ($categories as $id => $name) {
echo "{$name}";
}快速按主键查询find
方法有find(),findMany()
find($id) 通过主键查找单条记录,返回记录数组或 null
find($id, $key, true) 查找记录,找不到抛异常 ,返回记录数组
findMany($ids,$key) 通过主键数组查找多条记录 ,返回记录数组
findOrFail($id,$key) *已过时,使用 find($id, 'id', true)*,返回记录数组
基本用法
// 通过主键查找单条记录(默认找不到返回 null)
$user = $db->table('users')->find(1);
// 找不到会抛出异常(新方式)
try {
$user = $db->table('users')->find(999, 'id', true); // 第三个参数控制是否抛异常
} catch (Exception $e) {
echo "用户不存在";
}
// 兼容旧方式(已标记为过时)
try {
$user = $db->table('users')->findOrFail(999);
} catch (Exception $e) {
echo "用户不存在";
}
// 批量查找多条记录
$users = $db->table('users')->findMany([1, 2, 3, 4, 5]);
// 使用自定义主键字段
$user = $db->table('users')->find('admin@example.com', 'email');
// 自定义主键 + 抛异常
$user = $db->table('users')->find('admin@example.com', 'email', true);与传统方式对比
// 传统方式
$user = $db->table('users')->where('id', 1)->first();
$users = $db->table('users')->in('id', [1,2,3])->get();
// 使用 find 方法(更简洁)
$user = $db->table('users')->find(1);
$users = $db->table('users')->findMany([1,2,3]);分块遍历chunk()
chunk($count, $callback, $column = null),用于大数据量遍历:每批取 $count 行交给回调处理,再按推进列(默认主键 id)从上一批的最大值继续往后取,直到取完。每批只驻留 $count 行数据,内存占用与总行数无关。成功返回累计处理的行数。
参数说明:
$count每批行数,必须大于 0$callback回调函数,签名为function (array $rows),返回false可提前终止遍历$column推进列,默认id,需为唯一且可排序的列(通常是自增主键)
基本用法:
// 分批处理 5 万条订单,每批 1000 行
$total = Db::table('orders')->where('status', 1)->chunk(1000, function ($rows) {
foreach ($rows as $row) {
// 处理每一行数据……
}
});
echo "共处理 {$total} 行";回调返回 false 提前终止:
Db::table('logs')->chunk(500, function ($rows) {
foreach ($rows as $row) {
if ($row['level'] === 'error') {
return false; // 不再取下一批,遍历到此结束
}
}
});自定义推进列(表没有 id 列时):
Db::table('user_stats')->chunk(200, function ($rows) {
// ...
}, 'user_id');边遍历边写库:
// 按主键推进,遍历过程中更新或删除已处理的行不会漏行、不会重复
Db::table('users')->where('synced', 0)->chunk(1000, function ($rows) {
foreach ($rows as $row) {
Db::table('users')->where('id', $row['id'])->update(['synced' => 1]);
}
});在模型上使用(自动带上软删除范围):
$userModel = $this->model('User');
$userModel->where('status', 1)->chunk(1000, function ($rows) {
// 已软删除的记录不会被遍历到
});与事务配合,任意一批出错即整体回滚:
Db::transaction(function () {
Db::table('orders')->where('status', 1)->chunk(1000, function ($rows) {
foreach ($rows as $row) {
// 任意一步抛异常,整个事务回滚
}
});
});事务的完整用法(嵌套事务、inTransaction()、手动模式等)见 事务操作及维护。
注意事项:
- 不要用
limit/offset循环翻页来遍历全表:遍历过程中一旦有行被删除或插入,偏移量就会错位,导致已处理的行被跳过。chunk() 按推进列取值(WHERE id > 上一批最大值)推进,不受数据变动影响。 - 推进列必须唯一且可排序,默认主键
id;列名写错、或结果集里取不到该列时都会直接抛出异常,不会陷入死循环。 - 每批的
order / limit / offset由 chunk() 内部接管(按推进列 ASC + LIMIT $count),调用方不需要也不应再设置。 - chunk() 是「分批查询」,遍历过程中可以安全地写库或开启事务;而游标(unbuffered 流式读取)在同一连接上边读边写会报错,边读边写的场景请用 chunk()。
- chunk() 自身不包事务。需要「整批失败整体回滚」时把它放进
Db::transaction(function () { ... })里;但事务会持有锁,超大数据量建议每批单独提交。
和分页的关系
chunk() 和分页解决的不是同一个问题,但两者的推进方式同源:chunk() 内部用的 WHERE 推进列 > 上一批最大值 ORDER BY 推进列 ASC LIMIT $count,把「页码」换成「上一页最后一行的主键值」,就是游标分页(keyset pagination)。
按需求选:
- 要页码、要总数、能跳页(后台列表页)→
Db::table('t')->page($perPage, $page)取数据,或Model::paginate()一次拿到数据与total / total_pages / current_page / has_more,再用Pagination渲染页码。 - 只要「下一页」、翻得很深(无限滚动、导出)→ 游标分页,前端传上一页最后一条的主键(如
?last_id=xxx),不要传页码。代价是拿不到总数、不能跳页。 - 要遍历全表做处理(刷数据、导报表)→ chunk(),它没有「页」的概念,只有「批」。
为什么深翻页别用 OFFSET:LIMIT 10 OFFSET 19000 要先扫描并丢弃前 19000 行,读取行数随页码线性增长;游标写法恒定只读 10 行。2 万行实测:OFFSET 19000 读取 19010 行,游标读取 10 行。
分页的完整用法见 分页类,模型层的 paginate() 见 模型。
获取查询次数queryCount
返回查询的数据条数.
// 执行一系列查询操作
$db = Db::table('users')->where('status', 1)->get();
$articles = Db::table('articles')->limit(10)->get();
$category = Db::table('categories')->where('id', 5)->first();
// 获取已执行的查询总数
$count = Db::queryCount();
echo "执行的查询总数: " . $count; // 输出: 执行的查询总数: 3影响行数numRows()
返回数据影响条数.
// 查询示例
$users = Db::table('users')->where('status', 1)->get();
$affectedRows = Db::numRows();
echo "查询影响的行数: " . $affectedRows; // 例如: 查询影响的行数: 5
// 更新示例
Db::table('articles')->where('category_id', 3)->update(['status' => 0]);
$updatedRows = Db::numRows();
echo "更新影响的行数: " . $updatedRows; // 例如: 更新影响的行数: 8
// 删除示例
Db::table('comments')->where('post_id', 10)->delete();
$deletedRows = Db::numRows();
echo "删除影响的行数: " . $deletedRows; // 例如: 删除影响的行数: 12最新插入的id号insertId()
使用此方法,当插入数据后,可以查询最后插入的数据主键值(比如id值)
// 插入单条数据
$data = [
'title' => '新文章标题',
'content' => '文章内容',
'created_at' => date('Y-m-d H:i:s')
];
Db::table('articles')->insert($data);
$newArticleId = Db::insertId();
echo "新插入文章的ID: " . $newArticleId; // 例如: 新插入文章的ID: 25