laravel 中 union 必须用 db::table() 手动构造,eloquent 不支持;各子查询字段数、类型、顺序须严格一致,需 null 占位和显式别名,排序须在外层,且不支持直接分页。

联合查询(UNION)在 Laravel 中不能用 with()、join() 或关联方法实现,它本质是把多个独立查询的结果集纵向拼接,必须用 DB::table() + union() 手动构造;Eloquent 模型层不支持 UNION,强行套用会报错或静默失效。
UNION 必须用 DB::table(),不能用 Eloquent 模型
Eloquent 的 whereHas()、with()、join() 全部生成单条 SELECT 语句,而 UNION 是语法级操作,要求每个子查询结构一致(列数、类型、顺序)。模型类自带的表名、时间戳、软删除等逻辑会污染子查询结构,导致 union() 失败。
- ✅ 正确写法:
DB::table('users')->select('id', 'name', DB::raw('"user" as type'))→union()→DB::table('admins')->select('id', 'name', DB::raw('"admin" as type')) - ❌ 错误写法:
User::select('id', 'name')->union(Admin::select('id', 'name'))→ 报错:Call to undefined method Illuminate\Database\Eloquent\Builder::union() - 注意:所有子查询的
select()字段数必须严格一致,否则 MySQL 直接拒绝执行 - 字段别名(如
as type)建议统一加,避免结果列名丢失或冲突
union() 和 unionAll() 的区别与选型
MySQL 默认 UNION 去重,UNION ALL 不去重、性能更高;Laravel 提供两个方法对应,选哪个取决于业务是否允许重复行。
- 用
union():需要合并后去重,比如「用户 + 后台管理员」列表中同一人可能在两张表里,只显示一次 - 用
unionAll():确定无重复或不需要去重,比如「今日订单」+「昨日订单」,天然互斥,用unionAll()能省掉排序去重开销 - ⚠️ 性能影响:大数据量下
union()可能触发临时表 + 文件排序,unionAll()几乎无额外成本 - 无法在 union 后直接调用
paginate()—— Laravel 不支持对 union 结果分页,必须用子查询包装或手动处理 offset/limit
字段对齐和 NULL 填充是高频翻车点
两张表字段不一致时,必须用 NULL 或固定值占位,否则列数不匹配直接报错。常见于主键类型不同(UUID vs 自增 ID)、或某表多一个状态字段。
- 示例:users 表有
email,admins 表没有 → 在 admins 子查询里补DB::raw('NULL as email') - 类型要尽量兼容:字符串列对齐用
'',数字列用0或NULL,避免隐式转换导致数据截断 - 别名必须显式:不能依赖数据库自动推导,
select('users.name')和select('admins.nickname')必须都写成select('users.name as name')和select('admins.nickname as name') - 排序必须放最外层:
->union(...)->orderBy('name'),子查询里加orderBy会被忽略(除非配合LIMIT)
真正要用 UNION 的场景其实不多——大多数所谓“联合”其实是 JOIN 或搜索条件组合;一旦决定上 UNION,就得全程脱离 Eloquent,老老实实写 DB::table(),字段对齐、NULL 占位、外层排序这三步漏一步,查出来就是空或错数据。











