SQL 数字的 CTE table 对于静态值来说很快,但对于 table 值来说很慢

SQL CTE for a numbers table is fast for static value, but slow for table value

我试图根据 table 中的列多次输出 table 的值。

我尝试使用 CTE 即时生成数字 table:

WITH cte AS
(
    SELECT
        ROW_NUMBER() OVER (ORDER BY (select 0)) AS i
    FROM
        sys.columns c1 CROSS JOIN sys.columns c2 CROSS JOIN sys.columns c3
)
select *
from myTable, cte
WHERE i <= myTable.timesToRepeatColumn
  and myTable.id = '209386'

这个 SQL 似乎永远 运行,所以它似乎在加入之前试图 运行 整个 CTE。

如果我将 myTable.timesToRepeatColumn 替换为静态值(例如 10000),查询 returns 几乎是即时的。所以它似乎在完全交叉加入 CTE 的 table.

之前执行 where i <=

如何告诉 SQL 先执行 where 语句,就像它对静态数字所做的那样?

您可以使用递归 cte 来实现您的目标

WITH cte AS (
SELECT
  *
, timesToRepeatColumn as level
FROM
    myTablewhere 
WHERE myTable.id = '209386'
UNION ALL
SELECT
  *
, level -1  as level
FROM
    cte 
WHERE 
    level > 0
)


SELECT * FROM cte

SQL 服务器中的 CTE 不一定是 运行 'independently'。 SQL(在 SQL 服务器等中)是声明式的,这意味着您告诉它您想要什么,而不是如何去做。

如果查询优化器确定它可以通过做一些不同的事情来做得更好,它就会。

一个很好的例子是

IF EXISTS(SELECT * FROM test) PRINT 'X';
IF (SELECT COUNT(*) FROM test) > 0 PRINT 'Y';
IF (SELECT COUNT(*) FROM test) > 1 PRINT 'Z';

如果它按你说的做,第二个和第三个的查询计划基本上是一样的。但是,当你 运行 它时,第一个和第二个的查询计划是相同的;第三个不同。

当您 hard-code 值(例如 10,000)时,查询优化器可以使用该硬编码值来确定要执行的操作。在这种情况下,它可能确定它不需要 运行 完整的 CTE,只需 运行 它直到你得到 10,000 行。

但是,如果您使用一个可以变化的值(例如,myTable.timesToRepeatColumn),那么查询优化器通常会制定一个查询计划,该查询计划将使用 any 值.因此,它制定了一个不适合您的情况的查询计划 - 可能在使用它之前在内存中创建完整的 CTE。如果 sys.columns 有 100 行,那就是它创建的 100^3 行。如果是 1000,则为 1000^3,例如 1,000,000,000。您可能有超过 1000 行。