FULL OUTER JOIN和UNION ALL:MySQL里这俩怎么选?
FULL OUTER JOIN和UNION ALL:MySQL里这俩怎么选?
在MySQL中,FULL OUTER JOIN和UNION ALL是两种常用的数据合并方式,但它们在使用场景和性能上存在显著差异。本文将深入探讨这两种方法的优缺点、适用场景以及性能优化技巧,帮助开发者在实际项目中做出更合适的选择。
FULL OUTER JOIN的模拟实现
MySQL原生不支持FULL OUTER JOIN
,但我们可以用UNION
来模拟。简单来说,FULL OUTER JOIN
会把两个表的所有数据都拿出来,哪怕有一方匹配不到,也会用NULL
填充。
假设我们有两个表,一个是orders
存订单,一个是users
存用户,现在要把两张表里的数据合并,看看哪些订单没有匹配到用户,哪些用户没有订单。
SELECT * FROM (
SELECT users.*, orders.*
FROM users
LEFT JOIN orders ON users.id = orders.user_id
UNION
SELECT users.*, orders.*
FROM users
RIGHT JOIN orders ON users.id = orders.user_id
) AS combined
这样,所有订单和所有用户都会被展示出来,没匹配到的地方用NULL
填充。
UNION ALL的使用场景
如果你只是想把两个表的数据合并,但不在乎有没有重复数据,那UNION ALL
就很合适。
UNION ALL
不会去重,查询速度比UNION
快得多,适用于数据量大、不需要去重的场景,比如日志数据合并、历史记录查询等。
SELECT * FROM users
UNION ALL
SELECT * FROM orders
如果你是想模拟FULL OUTER JOIN
的效果,想要把所有数据都展示出来,还要填充NULL
,就用UNION
模拟。如果只是把两个表数据合起来,不在乎重复,就用UNION ALL
,速度快,SQL语句也简单。
可能遇到的问题和优化方案
1. 查询慢?加索引!
如果数据量大,FULL OUTER JOIN
模拟查询会很慢,因为UNION
需要遍历两次数据。加上索引可以提升性能:
CREATE INDEX idx_user_id ON users(id);
CREATE INDEX idx_order_user_id ON orders(user_id);
这样MySQL查询时会用索引加速匹配。
2. NULL太多?用IFNULL()填充默认值!
有些地方匹配不到会变成NULL
,如果不想看到一堆空值,可以用IFNULL()
设置默认值:
SELECT IFNULL(users.name, 'Unknown') AS user_name, orders.*
FROM users
LEFT JOIN orders ON users.id = orders.user_id
UNION
SELECT IFNULL(users.name, 'Unknown') AS user_name, orders.*
FROM users
RIGHT JOIN orders ON users.id = orders.user_id
这样查询出来的数据就不会出现NULL
,看起来更直观。
性能对比:FULL OUTER JOIN(模拟) vs. UNION ALL
因为 MySQL 原生不支持 FULL OUTER JOIN,所以我们只能用 LEFT JOIN + RIGHT JOIN + UNION 来模拟。这种方式的核心逻辑是:
- LEFT JOIN 取出左表的所有数据,并匹配右表,匹配不到的地方用 NULL 填充。
- RIGHT JOIN 取出右表的所有数据,并匹配左表,匹配不到的地方用 NULL 填充。
- UNION 合并两次查询的结果,去掉重复数据。
JOIN 操作的代价:这个查询执行了两次 JOIN,MySQL 需要先执行 LEFT JOIN 和 RIGHT JOIN,然后再 UNION,合并数据、去重。
去重(DISTINCT)的代价:UNION 默认会去重,因此 MySQL 在执行时会使用 TEMPORARY 表和 FILESORT,如果数据量大,可能会触发磁盘 IO,导致查询变慢。
索引优化:如果 customers.id 和 orders.customer_id 没有索引,查询性能会进一步下降。
如果你不需要去重,只想把两张表的数据简单合并,UNION ALL 是最快的选择,因为它:
- 不需要 JOIN,只需简单地把两张表的结果合并。
- 不需要去重,避免了 TEMPORARY 表和 FILESORT 操作。
总结
- 要完整匹配两张表,就用
FULL OUTER JOIN
(用UNION
模拟) - 要直接合并数据,不关心重复,就用
UNION ALL
- 数据量大,记得加索引,提高查询速度
- NULL太多,
IFNULL()
处理一下。
FULL OUTER JOIN 适合需要数据匹配的场景,但 MySQL 需要用 UNION 模拟,性能相对较慢。
UNION ALL 更快,更适合简单数据合并,但会包含重复数据,不适用于数据匹配场景。
优化技巧:加索引、避免 UNION 造成的去重开销、用 COALESCE 处理 NULL 值。如果还有不清楚的地方,欢迎留言讨论。