Eloquent不支持UNION ALL,需用DB::raw()手动拼接带别名的子查询并执行DB::select();注意字段数、类型对齐,避免SELECT *,返回结果非模型实例。

Union All在Eloquent里不能直接调用
Eloquent的union()方法底层调用的是SQL UNION,它默认去重;想保留重复行,必须绕过Eloquent封装,手动拼接UNION ALL。Laravel官方没提供unionAll()这样的API,硬写->union(...)->union(...)也没用——它还是生成UNION。
用DB::raw()拼接子查询实现Union All
核心思路是:把每个子查询构造成DB::raw('(...) AS t1')形式,再用原生SQL拼接。注意必须给每个子查询加别名(哪怕只是t1),否则MySQL报错Every derived table must have its own alias。
常见错误现象:SQLSTATE[42000]: Syntax error or access violation: 1248 Every derived table must have its own alias
- 先用
toSql()分别获取各子查询SQL,确认字段数、类型一致(否则UNION ALL会失败) - 用
DB::raw()包裹每个子查询,并强制加别名,例如:DB::raw('(SELECT id, name FROM users WHERE active = 1) AS u1') - 用
DB::select()执行拼好的完整SQL,不要试图塞进Model::query()链式调用里
示例:
立即学习“PHP免费学习笔记(深入)”;
$sql = "(SELECT id, name, 'user' as type FROM users WHERE status = 1)
UNION ALL
(SELECT id, title as name, 'post' as type FROM posts WHERE published = 1)";
$results = DB::select($sql);
字段对齐和类型兼容性必须人工校验
MySQL的UNION ALL要求各子查询返回列数相同、对应列类型兼容(如不能一个VARCHAR对一个INT)。Eloquent模型的select()如果漏字段、或用了as别名不一致,运行时才报错,很难调试。
- 所有子查询显式写出字段名,避免
SELECT *—— 字段顺序错位会导致数据错乱 - 字符串字段补
CAST(... AS CHAR),数字字段补CAST(... AS SIGNED),确保跨表类型对齐 - 如果某张表没有某个字段,用
NULL AS field_name占位,类型也要匹配(比如NULL默认是TEXT,需CAST(NULL AS CHAR))
性能敏感场景慎用,尤其带ORDER BY或LIMIT
原生UNION ALL无法直接在整体结果上做ORDER BY或LIMIT,除非包一层外层查询。但这样会让MySQL无法利用索引,全表扫描风险高。
使用场景:适合结果集不大、或后续还要PHP处理的联合需求;如果要分页或按联合结果排序,优先考虑合并逻辑到应用层(比如array_merge两个get()结果),反而更可控。
容易被忽略的地方:UNION ALL本身不走Eloquent的模型映射,返回的是stdClass对象数组,不是模型实例,关联预加载、访问器、强制类型转换全部失效。需要手动映射或改用collect($results)->mapInto(User::class)这类方式补救。



















