JOIN then UNION vs. UNION then JOIN 的表现
Posted
技术标签:
【中文标题】JOIN then UNION vs. UNION then JOIN 的表现【英文标题】:Performance of JOIN then UNION vs. UNION then JOIN 【发布时间】:2020-09-16 09:00:19 【问题描述】:我有一个 SQL 查询,内容如下:
WITH a AS (
SELECT *
FROM table1
INNER JOIN table3 ON table1.id = table3.id
WHERE table1.condition = 'something'
),
b AS (
SELECT *
FROM table2
INNER JOIN table3 ON table2.id = table3.id
WHERE table2.condition = 'something else'
),
combined AS (
SELECT *
FROM a
UNION
SELECT *
FROM b
)
SELECT *
FROM combined
我把它改写为:
WITH a AS (
SELECT *
FROM table1
WHERE table1.condition = 'something'
),
b AS (
SELECT *
FROM table2
WHERE table2.condition = 'something else'
),
combined AS (
SELECT *
FROM (
SELECT *
FROM a
UNION
SELECT *
FROM b
) union
INNER JOIN table3 ON union.id = table3.id
)
SELECT *
FROM combined
我预计这可能会更高效,因为它只执行JOIN
一次,或者至少不会影响执行时间。我惊讶地发现,现在查询的运行时间几乎是原来的两倍。
这没问题,因为它以前运行得很好,我只是出于个人风格偏好而重新编写了它,所以我会坚持原来的。但我不是数据库/SQL 方面的专家,所以我很想知道是否有人可以分享关于为什么第二种方法的性能如此低下的任何见解?
如果有什么不同,那就是 Redshift 数据库,table1
和 table2
的行数都约为 2.5 亿行,table3
的行数约为 100 万行,combined
的行数不到 1000 行。
【问题讨论】:
您需要为这两个查询生成执行计划并查看它们的不同之处。在general 中,如果两个查询在逻辑上产生相同的结果,则优化器应该 生成相同的计划。因为在 SQL 中,你告诉系统你想要什么,而不是如何去做。 当然第一个更高效:两个连接可以使用索引和统计信息。在 CTE 之后,所有表结构都丢失了,一切都简化为堆表扫描和散列连接。 @wildplasser 除非我没有经验专门针对亚马逊红移,否则我认为这不是真的。 CTE 只不过是一个表达式,所有索引和统计信息都会持续到该表达式的最终使用。 @GarethD 在 postgres SQL 中,CTE 是一个优化障碍,至少在 PG-12 (IIRC) 之前是这样。 Redshift 是一些较旧的(可能是残废的)PG 版本。 @wildplasser 很有趣。我的立场是正确的。 【参考方案1】:SQL 优化器在“裸”表上的信息比在“计算”表上的信息多。因此,这两个 CTE 更容易优化。
在使用索引的数据库中,这可能会影响索引的使用。在 Redshift 中,这可能会导致额外的数据移动。
不过,在这种特殊情况下,我怀疑问题可能与通过 JOIN
操作进行过滤有关。 UNION
会产生删除重复项的开销。通过在UNION
之前过滤,重复删除比之后过滤更快。
此外,UNION
可能会影响数据的位置,因此第二个版本可能需要额外的数据移动。
【讨论】:
以上是关于JOIN then UNION vs. UNION then JOIN 的表现的主要内容,如果未能解决你的问题,请参考以下文章