了解 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 或最终语句,例如 SELECT
或 UPDATE
。
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
,会破坏这个概念。
当我在 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 或最终语句,例如 SELECT
或 UPDATE
。
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
,会破坏这个概念。