了解 CTE 分号放置

Understanding CTE Semicolon Placement

当我在 SQL 服务器中 运行 这个 CTE 时,它说 declare 语句的语法不正确。

;WITH cte as
(
SELECT tblKBFolders.FolderID
from tblKBFolders
where FolderID = @FolderID

UNION ALL

SELECT tblKBFolders.FolderID
FROM tblKBFolders  
INNER JOIN cte
ON cte.FolderID = tblKBFolders.ParentFolderID
)

declare @tblQueryFolders as table (FolderID uniqueidentifier)
insert into @tblQueryFolders
SELECT FolderID From cte;

但是如果我将 declare 移动到 CTE 之前,它 运行 就好了。

declare @tblQueryFolders as table (FolderID uniqueidentifier)

;WITH cte as
(
SELECT tblKBFolders.FolderID
from tblKBFolders
where FolderID = @FolderID

UNION ALL

SELECT tblKBFolders.FolderID
FROM tblKBFolders  
INNER JOIN cte
ON cte.FolderID = tblKBFolders.ParentFolderID
)

insert into @tblQueryFolders
SELECT FolderID From cte;

这是为什么?

你问的答案已经在评论中给出了:这与分号的位置无关。

重要提示:CTE 的 WITH 不能紧跟在没有结束分号的语句之后。有许多语句,其中 WITH 子句会在语句末尾添加一些内容(查询提示,OPENJSON 之后的 WITH 等)。引擎将不得不猜测,这个 WITH 是否添加到之前的语句中,或者它是否是 CTE 的开始。这就是为什么我们经常看到

;WITH cte AS (...)

这实际上是分号的错误用法。人们把它放在那里,只是为了不忘记它。无论如何,它被视为更好的风格和 最佳实践 结束 T-SQL 语句 总是 用分号(并做 使用;WITH,因为它实际上添加了一个空语句)。

CTE 只不过是语法糖。将 CTE 的代码放在 FROM(SELECT ...) AS SomeAlias 中大致相同。在大多数情况下,这将导致相同的执行计划。在您必须在多个地方编写相同的 FROM(SELECT ) AS SomeAlias 的情况下,它会有所帮助。而且——总的来说——它让事情更容易阅读和理解。但无论如何,它都不能与 temp table 或 table 变量相提并论。引擎会将其视为内联代码,您可以在同一语句中独占使用它。

所以这是一样的:

WITH SomeCTE AS(...some query here...)
SELECT SomeCTE.* FROM SomeCTE;


SELECT SomeAlias.* 
FROM (...some query here...) AS SomeAlias;

您的示例看起来像是您将 CTE 视为一种 临时 table 定义,您可以在以下语句中使用它。但这是不正确的。

在 CTE 之后,引擎需要另一个 CTE 或最终语句,例如 SELECTUPDATE

WITH SomeCTE AS(...some query here...)
SELECT * FROM SomeCTE;

WITH SomeCTE AS( ...query... )
    ,AnotherCTE AS ( ...query... )
SELECT * FROM AnotherCTE;

...或添加了WITH子句的其他内容:

WITH XMLNAMESPACES( ...namespace declarations...)
    ,SomeCTE AS( ...query... )
SELECT * FROM SomeCTE;

所有这些示例都是一个单独的语句

在中间放一个DECLARE @Something,会破坏这个概念。