为什么在此查询中使用 CTE 比使用 #Temp 表快得多?

Jua*_*lez 2 sql-server

我有 2 个不同的查询来识别重复(如下)。两个查询之间的唯一区别是一个使用 CTE,另一个使用 #Temp 表。

有谁知道为什么 CTE 比 #Temp 表快得多(0:20 秒 VS. 1:22)?

我宁愿使用 CTE,但我需要使用 CTE 运行 2 个 STATEMENTS(从 CTE 删除,然后从 CTE 插入表),但 SQL Server 只允许您在 CTE 上编写一个 STATEMENT。

查询 1:

WITH DUPS AS(
   Id,
   Column1,
   Column2,
   Column3,
   RN = ROW_NUMBER()OVER(PARTITION BY ID ORDER BY id)
   FROM mytable
)
  Select top 1 * FROM DUPS WHERE RN > 1 
Run Code Online (Sandbox Code Playgroud)

查询 2:

 SELECT    
           Id,
           Column1,
           Column2,
           Column3,
           RN = ROW_NUMBER()OVER(PARTITION BY ID ORDER BY id)
       INTO #DUPS
       FROM mytable

       Select top 1 * FROM #DUPS WHERE RN > 1 
Run Code Online (Sandbox Code Playgroud)

Eri*_*rik 6

我相信,如果您查看执行计划,您会发现 CTE 为您提供了更好的计划,因为它可以更好地利用TOP. 临时表必须在 TempDB 中创建表,将所有数据保存到磁盘,然后选择顶部记录。如果您TOP从两个查询中删除 ,我敢打赌您会获得更接近的性能。话虽如此,我通常希望 CTE 在像您这里这样的单次使用场景中执行临时表,因为 CTE 所做的工作比创建/插入临时表少。

这可能只是示例代码,但值得一提的是,您的结果是不确定的,因为您TOP没有使用ORDER BY子句。

如需进一步阅读,您应该查看此问题这个问题的答案似乎特别适用于您的情况。


对于您问题的隐含后半部分:

既然您说您需要至少两次使用来自 CTE 的数据,那么您有四个选择:

  1. 将您的 CTE 复制并粘贴到两个地方
  2. 使用 CTE 将数据插入到表变量中,并使用表变量中的数据执行接下来的两个操作。
  3. 使用 CTE 将数据插入到临时表中,并使用临时表中的数据执行接下来的两个操作。
  4. 编写更好的定制 CTE。我假设您正在做不同的事情,因此查询必须略有不同。

我倾向于选择选项 2(表变量)或选项 4(定制的 CTE)方法。我不喜欢复制/粘贴 CTE 的重复和额外维护。我也喜欢在临时表上显式缩小表变量的范围。有关表变量和临时表之间差异的权威处理,请查看此答案。至于在选项 2 和 4 之间进行选择;这将取决于您的分析规定和/或您必须在哪些限制范围内工作。

  • 我从两个查询中删除了 TOP,CTE 仍然运行了 22 秒,但 #Temp 表的运行速度实际上快了大约 30 秒。因此,正如您所指出的,TOP 肯定会对#TEMP 表的处理时间产生影响。 (2认同)