查询构造器

Viswoole 的查询构造器提供流畅的链式 API,用于构建和执行数据库查询。通过 Db::table() 获取查询构造器实例。

获取查询构造器

php
use Viswoole\Database\Facade\Db;

// 获取指定表的查询构造器
$query = Db::table('users');

// 结合通道切换使用
$query = Db::channel('read')->table('users');

新增数据 (Create)

insert — 插入单条记录

php
Db::table('users')->insert([
    'name' => '张三',
    'email' => 'zhangsan@example.com',
    'status' => 1,
]);

insertGetId — 插入并返回 ID

当需要获取自增主键时使用此方法:

php
$userId = Db::table('users')->insertGetId([
    'name' => '张三',
    'email' => 'zhangsan@example.com',
]);

echo "新用户 ID: {$userId}";  // 输出插入记录的主键值

查询数据 (Read)

find — 主键查找

根据主键查询单条记录,返回 DataSet 对象。注意:即使未查询到记录也会返回一个空的 DataSet 对象(而非 null),空 DataSet 在 PHP 中恒为 truthy,因此判断是否有记录应使用 isEmpty()

php
// 按 ID 查找
$user = Db::table('users')->find(1);

// 正确判断是否查询到记录
if (!$user->isEmpty()) {
    echo $user->name;
}

// 也可以传入 false 禁用空结果返回(此时无记录返回 null)
$user = Db::table('users')->find(1, false);
if ($user) {
    echo $user->name;
}

first — 获取第一条记录

返回符合条件的第一条记录:

php
$user = Db::table('users')
    ->where('status', 1)
    ->orderBy('created_at', 'desc')
    ->first();

select — 获取多条记录

返回符合条件的所有记录集合:

php
$users = Db::table('users')
    ->where('status', 1)
    ->select();

foreach ($users as $user) {
    echo $user->name;
}

getArray — 数组形式返回

以纯数组形式返回查询结果(非对象):

php
$users = Db::table('users')
    ->where('status', 1)
    ->getArray();

// 返回格式: [['id' => 1, 'name' => '张三'], ...]

cursor — 生成器游标

适用于大数据量查询,通过生成器逐条读取,降低内存占用:

php
// 逐条处理,内存占用恒定
foreach (Db::table('users')->cursor() as $user) {
    // 处理每条记录...
}

注意cursor() 返回的是 Generator 对象,适合一次性处理大量数据。

chunk — 分块处理

将结果按指定数量分批处理,适用于数据迁移、批量导出等场景。chunk() 方法签名为 chunk(int $size): Generator,仅接收分块大小参数,返回一个生成器,每次产出一个 Collection 集合:

php
// 每次查询 100 条记录,生成器逐批产出 Collection
foreach (Db::table('users')->chunk(100) as $collection) {
    foreach ($collection as $user) {
        // 处理当前批次的每条记录
    }
}

注意chunk() 的参数 $size 是每次查询的批次大小,生成器会自动翻页并逐批产出 Collection 对象。

更新数据 (Update)

php
// 更新指定条件的数据
$affectRows = Db::table('users')
    ->where('id', 1)
    ->update(['name' => '李四', 'email' => 'lisi@example.com']);

echo "影响行数: {$affectRows}";

// 多条件更新
Db::table('users')
    ->where('status', 0)
    ->where('last_login', '<', '2025-01-01')
    ->update(['status' => -1]);

删除数据 (Delete)

php
// 按条件删除
$affectRows = Db::table('users')
    ->where('id', 1)
    ->delete();

echo "删除行数: {$affectRows}";

// 批量删除
Db::table('users')
    ->whereIn('id', [10, 11, 12])
    ->delete();

聚合查询

方法说明示例
count()统计记录数Db::table('users')->count()
avg()计算平均值Db::table('users')->avg('age')
sum()求和Db::table('orders')->sum('amount')
min()最小值Db::table('users')->min('created_at')
max()最大值Db::table('users')->max('created_at')
php
// 统计用户总数
$total = Db::table('users')->count();

// 计算平均年龄
$avgAge = Db::table('users')->avg('age');

// 订单总金额
$totalAmount = Db::table('orders')->sum('amount');

// 带条件的聚合统计
$activeUserCount = Db::table('users')
    ->where('status', 1)
    ->count();

条件查询

基础 where

php
// 等值查询
->where('status', 1)

// 指定运算符
->where('age', '>=', 18)

// 数组形式(AND 条件)— 请使用 wheres() 方法
->wheres([
    'name' => '张三',
    'status' => 1,
])

注意where() 第一个参数为 string 类型,不支持传入数组。如需批量设置多个 AND 条件,请使用 wheres(array $wheres) 方法。

orWhere — 或条件

php
Db::table('users')
    ->where('status', 1)
    ->orWhere('role', 'admin')
    ->select();
-- SQL: WHERE status = 1 OR role = 'admin'

whereIn — IN 查询

php
->whereIn('id', [1, 2, 3, 4, 5])

->whereNotIn('id', [99, 100])  // NOT IN

whereBetween — 区间查询

php
->whereBetween('age', [18, 60])

->whereNotBetween('age', [0, 17])  // NOT BETWEEN

whereNull / whereNotNull — 空值判断

php
->whereNull('deleted_at')       // IS NULL
->whereNotNull('email')         // IS NOT NULL

whereGroup — 条件分组(嵌套)

用于构建复杂的括号嵌套条件。whereGroup(array $wheres, string $connector = 'AND') 接收一个条件数组,数组元素支持两种形式:

  • 关联键值对['字段' => 值],默认使用 = 运算符和 AND 连接符
  • 索引数组['字段', '运算符', '值', '连接符'],第 4 个元素为连接符 AND/OR(可选,默认 AND
php
Db::table('users')
    ->whereGroup([
        ['status', '=', 1],
        ['role', '=', 'admin', 'OR'],
        ['role', '=', 'super_admin', 'OR'],
    ])
    ->select();
-- SQL: WHERE (status = 1 AND role = 'admin' OR role = 'super_admin')

如需嵌套分组,可以传入第二个参数指定该组与外部条件的连接方式:

php
Db::table('users')
    ->where('type', 'vip')
    ->whereGroup([
        ['status', '=', 1],
        ['role', '=', 'admin', 'OR'],
    ], 'AND')
    ->select();
-- SQL: WHERE type = 'vip' AND (status = 1 AND role = 'admin')

whereRaw — 原始表达式

当需要使用数据库特有函数或复杂表达式时:

php
// JSON 字段查询
->whereRaw('JSON_CONTAINS(tags, ?)', ['["php"]'])

// 日期函数
->whereRaw('DATE(created_at) = ?', ['2025-06-22'])

原始表达式

使用 Db::raw() 创建原始 SQL 表达式,避免被作为字符串转义:

php
use Viswoole\Database\Facade\Db;

// 使用 NOW() 函数
Db::table('users')->insert([
    'name' => '张三',
    'created_at' => Db::raw('NOW()'),
]);

// 自增字段
Db::table('users')
    ->where('id', 1)
    ->update([
        'login_count' => Db::raw('login_count + 1'),
    ]);

连接查询

join — 内连接

join() 签名:join(string $table, string $localKey, string $foreignKey, string $operator = '=', string $type = 'INNER'),注意运算符在第 4 个参数位置:

php
Db::table('users u')
    ->join('orders o', 'u.id', 'o.user_id')
    ->columns('u.name', 'o.amount', 'o.created_at')
    ->select();

leftJoin — 左连接

注意leftJoin() 的签名与 join() 不同:leftJoin(string $table, string $localKey, string $operator, string $foreignKey),运算符在第 3 个参数位置。

php
Db::table('users u')
    ->leftJoin('profiles p', 'u.id', '=', 'p.user_id')
    ->columns('u.*', 'p.avatar', 'p.bio')
    ->select();

rightJoin / fullJoin

rightJoin()fullJoin() 的签名与 join() 一致:运算符在第 4 个参数位置。

php
// 右连接
->rightJoin('departments d', 'u.dept_id', 'd.id')

// 全连接
->fullJoin('extensions e', 'u.id', 'e.user_id')

排序与分页

orderBy — 排序

php
// 单字段排序
->orderBy('created_at', 'desc')

// 多字段排序
->orderBy('status', 'asc')
->orderBy('created_at', 'desc')

page / limit / offset — 分页

php
// 分页查询(页码, 每页条数)
->page(1, 20)

// 等价于
->limit(20)->offset(0)

// 第二页
->page(2, 20)
-- LIMIT 20 OFFSET 20

distinct — 去重

php
// 获取不重复的角色列表
Db::table('users')
    ->columns('role')
    ->distinct()
    ->select();

其他选项

columns 字段选择

注意:字段选择应使用 columns(string...$column): static 方法。select(bool $allowEmpty = true) 是执行查询的方法,不接受字段参数。

php
// 选择指定字段
->columns('id', 'name', 'email')

// 别名
->columns('id', Db::raw('COUNT(*) as total'))

groupBy / having — 分组

php
Db::table('orders')
    ->columns('user_id')
    ->columns(Db::raw('COUNT(*) as order_count'))
    ->columns(Db::raw('SUM(amount) as total_amount'))
    ->groupBy('user_id')
    ->having('order_count', '>', 5)
    ->select();

alias — 表别名

php
Db::table('users')
    ->alias('u')
    ->columns('u.id', 'u.name')
    ->select();

锁机制

lockForUpdate — 悲观锁(排他锁)

用于防止并发修改,事务提交前其他事务无法修改或读取该记录:

php
Db::startTransaction(function () {
    $user = Db::table('users')
        ->where('id', 1)
        ->lockForUpdate()
        ->first();

    // 其他事务在此期间无法修改该记录
    $user->balance -= 100;
    Db::table('users')->where('id', 1)->update([
        'balance' => $user->balance
    ]);
});

sharedLock — 共享锁

允许其他事务读取但不允许修改:

php
$user = Db::table('users')
    ->where('id', 1)
    ->sharedLock()
    ->first();

完整示例

php
use Viswoole\Database\Facade\Db;

// 复杂查询示例:统计每个分类下活跃用户的订单总额
$result = Db::table('users u')
    ->alias('u')
    ->leftJoin('orders o', 'u.id', '=', 'o.user_id')
    ->columns(
        'u.category',
        Db::raw('COUNT(DISTINCT u.id) as user_count'),
        Db::raw('COALESCE(SUM(o.amount), 0) as total_amount')
    )
    ->where('u.status', 1)
    ->whereGroup([
        ['u.role', '=', 'vip', 'OR'],
        ['u.level', '>=', 5, 'OR'],
    ])
    ->whereNotNull('u.email')
    ->groupBy('u.category')
    ->having('total_amount', '>', 1000)
    ->orderBy('total_amount', 'desc')
    ->page(1, 20)
    ->getArray();