为什么优化器决定 self-join a table?
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
);
它returns其中未完成任务数量最多的项目的标题。
这是它的执行计划:
我想知道为什么它的第 6-8 步看起来像项目 table 的 self-join?并且它将连接的结果存储为视图,但是根据行和字节列的视图与项目 table 相同。他为什么要这么做?
我还想知道第 2 步和第 1 步代表什么。我想,2 存储我的 CTE 结果以在步骤 10-14 中使用它,1 从视图中删除不具有子查询返回的 'cnt' 值的行,这是正确的猜测吗?
除了上面的评论之外,当您不止一次引用 CTE 时,有一个启发式告诉优化器具体化 CTE,这就是为什么您会看到临时 table 转换。
关于此查询的其他一些 comments/questions。我假设关系是一个 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 函数时通常效率更高。这样你就可以消除自连接。当您有数百万或数十亿行时,这会产生很大的性能差异。因此,例如,您的查询可以重写为:
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
我正在分析我的查询,如下所示:
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
);
它returns其中未完成任务数量最多的项目的标题。
这是它的执行计划:
我想知道为什么它的第 6-8 步看起来像项目 table 的 self-join?并且它将连接的结果存储为视图,但是根据行和字节列的视图与项目 table 相同。他为什么要这么做?
我还想知道第 2 步和第 1 步代表什么。我想,2 存储我的 CTE 结果以在步骤 10-14 中使用它,1 从视图中删除不具有子查询返回的 'cnt' 值的行,这是正确的猜测吗?
除了上面的评论之外,当您不止一次引用 CTE 时,有一个启发式告诉优化器具体化 CTE,这就是为什么您会看到临时 table 转换。
关于此查询的其他一些 comments/questions。我假设关系是一个 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 函数时通常效率更高。这样你就可以消除自连接。当您有数百万或数十亿行时,这会产生很大的性能差异。因此,例如,您的查询可以重写为:
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