Understanding CTE Semicolon Placement

孤街浪徒 提交于 2020-07-10 10:27:20

问题


When I run this CTE in SQL Server it says the syntax is incorrect by the declare statement.

;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;

But if I move the declare to before the CTE, it runs just fine.

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;

Why is that?


回答1:


The answer you ask for was given in a comment already: This has nothing to do with the semicolon's placement.

Important: The CTE's WITH cannot follow right after a statement without an ending semicolon. There are many statments, where a WITH-clause would add something to the end of the statement (query hints, the WITH after OPENJSON etc.). The engine would have to guess, whether this WITH adds to the statment before or if it is a CTE's start. That's the reason, why we often see

;WITH cte AS (...)

That's actually the wrong usage of a semicolon. People put it there, just not to forget about it. Anyway it is seen as better style and best practice to end T-SQL statements always with a semicolon (and do not use ;WITH, as it adds an empty statement actually).

A CTE is not much more than syntactical sugar. Putting the CTE's code within a FROM(SELECT ...) AS SomeAlias would be roughly the same. In most cases this would lead to the same execution plan. It helps in cases, where you'd have to write the same FROM(SELECT ) AS SomeAlias in multiple places. And - in general - it makes things easier to read and understand. But it is not - by any means - comparable to a temp table or a table variable. The engine will treat it as inline code and you can use it in the same statement exclusively.

So this is the same:

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


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

Your example looks like you think of the CTE as kind of a temp table definition, which you can use in the following statements. But this is not correct.

After the CTE the engine expects another CTE or a final statement like SELECT or UPDATE.

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

or

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

...or another content added with the WITH clause:

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

All of these examples are one single statement.

Putting a DECLARE @Something in the middle, would break this concept.



来源:https://stackoverflow.com/questions/62659552/understanding-cte-semicolon-placement

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!