[英]Why does the optimizer decide to self-join a table?
我正在分析我的查询,如下所示:
WITH Project_UnfinishedCount AS (
SELECT P.Title AS title, COUNT(T.ID) AS cnt
FROM PROJECT P LEFT JOIN TASK T on P.ID = T.ID_PROJECT AND T.ACTUAL_FINISH_DATE IS NULL
GROUP BY P.ID, P.TITLE
)
SELECT Title
FROM Project_UnfinishedCount
WHERE cnt = (
SELECT MAX(cnt)
FROM Project_UnfinishedCount
);
它返回具有最多未完成任务的项目的标题。
我想知道为什么它的步骤 6-8 看起来像项目表的自连接? 并且它将连接的结果存储为视图,但视图,根据行和字节列与项目表相同。 他为什么这样做?
我也想知道 2 和 1 步骤代表什么。 我猜,2 存储我的 CTE 的结果以在步骤 10-14 中使用它,而 1 从视图中删除没有子查询返回的“cnt”值的行,这是正确的猜测吗?
除了上面的注释之外,当您多次引用 CTE 时,还有一个启发式方法告诉优化器具体化 CTE,这就是您看到临时表转换的原因。
关于此查询的其他一些评论/问题。 我假设这种关系是一个 PROJECT 可以有 0 个或多个任务,并且每个 TASK 只针对一个 PROJECT。 在那种情况下,我想知道为什么你有一个外部连接? 此外,您将加入 ACTUAL_FINISH_DATE 列。 这意味着如果您有一个项目,其中所有任务都已完成,那么外部联接将实现不匹配的行,这将使您的查询结果看起来表明有 1 个未完成的任务。 所以我认为你的 CTE 应该看起来更像:
SELECT P.Title AS title, COUNT(T.ID) AS cnt
FROM PROJECT P
JOIN TASK T on P.ID = T.ID_PROJECT
WHERE T.ACTUAL_FINISH_DATE IS NULL
GROUP BY P.ID, P.TITLE
话虽如此,这些“在组中查找匹配项(计数、最大值等)”类型的查询在编写为 window function 时通常更有效。 这样你就可以消除自我加入。 当您拥有数百万或数十亿行时,这可能会产生很大的性能差异。 因此,例如,您的查询可以重写为:
SELECT TITLE, CNT
FROM (
SELECT P.Title AS title, COUNT(T.ID) AS cnt
, RANK() OVER( ORDER BY COUNT(*) DESC ) RNK
FROM PROJECT P
JOIN TASK T on P.ID = T.ID_PROJECT
WHERE T.ACTUAL_FINISH_DATE IS NULL
GROUP BY P.ID, P.TITLE
)
WHERE RNK=1
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.