为什么 CTE 应该以分号开头?

Han*_*non 16 sql-server syntax cte t-sql

我只是在看StackOverflow上的一篇文章,其中 Aaron Bertrand 建议使用 CTE 而不是数字表,这是执行手头任务的一种优雅方式。我的问题是,为什么 CTE 的第一行以分号开头?

;WITH n AS (SELECT TOP (10000) n FROM 
  (SELECT n = ROW_NUMBER() OVER
    (ORDER BY s1.[object_id])
    FROM sys.all_objects AS s1
    CROSS JOIN sys.all_objects AS s2
  ) AS x ORDER BY n
)
SELECT n FROM n ORDER BY n; -- look ma, no gaps!
Run Code Online (Sandbox Code Playgroud)

这是为了确保 WITH 语句不会被解析为以前的内容SELECT或其他内容吗?我在 SQL Server 2005 BOL 中没有看到关于在 WITH 之前使用分号的内容。

Aar*_*and 30

我总是在此处或 StackOverflow 上发帖时这样做,因为因为WITH- 由于关键字过载 - 上一个命令需要终止分号。如果我粘贴一个使用 CTE 的代码示例,不可避免地会有一些用户将它粘贴到他们现有的代码中,并且前面的语句不会有分号。所以代码中断了,我收到如下抱怨:

你的密码坏了!我收到此错误消息:

Incorrect syntax near 'WITH'...

虽然我愿意相信人们总是用分号结束他们的陈述会变得更好,但我宁愿预先排除噪音并始终包含它。有些人不喜欢它,但是<shrug />。您可以根据需要在有效语句之前或之后包含任意数量的分号。这是有效的:

;;;;SELECT 1;;;;;;;;;;;;SELECT 2;;;;;;;;SELECT 3;;;;;
Run Code Online (Sandbox Code Playgroud)

因此,在定义需要它的语句之前有一个额外的分号并没有什么害处。即使它不那么漂亮,这样做也更安全。

必须用奇怪的措辞才能理解这一点,但自 SQL Server 2008 以来,“不以分号结束有效语句”实际上已被弃用。因此,正如我在博客文章中所述,我链接到上面,即使在以下情况下它不需要绕过错误,它应该在任何有效的地方使用。你可以在这里看到这个(这个条目已经存在多个版本):

如果没有例外,当然不会是 SQL Server。尝试这个:

BEGIN TRY;
  SELECT 1/1;
END TRY;
BEGIN CATCH;
  SELECT 1/1;
END CATCH;
Run Code Online (Sandbox Code Playgroud)

这不是规则的唯一例外,但它是我认为最不直观的例外。


swa*_*eck 11

这是为了确保它不包含在任何先前的语句中,因为它WITH可以在 T-SQL 中用于多种目的。

如果它是批处理中的第一条语句,我认为您不需要它。


Pau*_*ite 5

为什么 CTE 应以分号开头?

不应该。之前的语句应该正确终止。

从文档中WITH common_table_expression

  • 当在属于批处理的语句中使用 CTE 时,其前面的语句后面必须跟有分号。

如果您出于某种原因决定使用额外的语句终止符,我建议将终止符放在单独的行上,并用注释解释为什么它在那里:

; -- Previous statement must be properly terminated
WITH n AS (SELECT TOP (10000) n FROM 
  (SELECT n = ROW_NUMBER() OVER
    (ORDER BY s1.[object_id])
    FROM sys.all_objects AS s1
    CROSS JOIN sys.all_objects AS s2
  ) AS x ORDER BY n
)
SELECT n FROM n ORDER BY n;
Run Code Online (Sandbox Code Playgroud)

这可以避免误导人们认为它是公用表表达式语法的一部分,但事实并非如此。