使用Union All与Left Join多表查询:哪种SQL语句性能更优?
我有一个包含booking_id、booking_type字段的booking表,该表通过外键booking_id关联booking_taxi表与booking_bus表。各表结构如下:
booking:booking_id|booking_typebooking_taxi:booking_taxi_id|booking_id|booking_datebooking_bus:booking_vus_id|booking_id|booking_date
我编写了两条SQL语句,用于获取所有预订记录及其对应的预订日期:
Query 1:
select bk.booking_id, bk.booking_type, case when booking_type = 3 then bbus.booking_date when booking_type = 2 then btaxi.pickup_date end as booking_date from booking bk left join booking_taxi btaxi on btaxi.booking_id = bk.booking_id and bk.booking_type = 2 left join booking_bus bbus on bbus.booking_id = bk.booking_id and bk.booking_type = 3;
Query 2:
select bk.booking_id, bk.booking_type, btaxi.booking_date from booking bk inner join booking_taxi btaxi on btaxi.booking_id = bk.booking_id and bk.booking_type = 2 union all select bk.booking_id, bk.booking_type, bbus.booking_date from booking bk inner join booking_bus bbus on bbus.booking_id = bk.booking_id and bk.booking_type = 3;
请问哪条语句的性能更优?
通常来说,Query 2的性能会更优,核心原因可以从这几个维度分析:
连接逻辑与数据过滤效率:
Query 1采用LEFT JOIN,即便连接条件里加了booking_type的过滤,数据库仍需先把booking表的所有记录和另外两个表完成连接匹配,再通过CASE语句筛选对应日期。如果booking表里存在大量不属于类型2或3的记录,这些无效连接会额外消耗内存和IO资源。
而Query 2用INNER JOIN+UNION ALL的组合,会先分别筛选出booking表中类型为2和3的记录,各自与对应子表做内连接(只保留匹配成功的行),再合并结果。内连接本身比左连接更高效,因为它不会保留不匹配的冗余行,直接减少了中间处理的数据量。结果集处理成本:
Query 1的结果集会包含booking表的所有记录,哪怕那些没有对应出租车/巴士预订的记录(日期字段为NULL)。如果业务只需要有对应日期的有效预订,这部分无效数据完全是多余的处理开销。
Query 2合并的两个结果集都是有效匹配的记录,没有冗余的NULL行,数据库在计算和返回结果时不需要额外处理这些无效数据,成本更低。索引利用效率:
假设你的booking表在booking_type和booking_id上有复合索引,booking_taxi和booking_bus在booking_id上有索引,Query 2的两个子查询可以更精准地利用索引:先快速定位booking表中类型为2/3的行,再通过booking_id关联子表。而Query 1的左连接因为要保留所有booking行,可能会降低索引的利用效率,尤其是当booking表数据量很大的时候。
不过要注意:如果你的业务需求是必须包含所有booking记录(哪怕没有对应日期),那Query 2就不适用了,因为它只返回有对应子表数据的记录。这种情况下Query 1是唯一选择,但你可以考虑给booking_type字段添加索引,来优化连接条件的过滤效率。
内容的提问来源于stack exchange,提问作者Chinthaka Fernando

