理解QUOTED_IDENTIFIER

14

我们刚刚遇到了一个问题,其中一个存储过程抛出了错误;

SELECT failed because the following SET options have incorrect settings: 'QUOTED_IDENTIFIER'

我通过修改存储过程并将引号标识符设置为ON来解决了这个问题。问题是,在CREATE PROCEDURE调用之前,我就已经做了这个操作。例如:

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE [dbo].[InsertStuff]

我会认为这会影响CREATE PROCEDURE语句,但不会影响该过程的执行。

我们的脚本都是作为删除和创建脚本部署并通过sqlcmd运行的。我刚刚读到这里(搜索示例:执行SQLCMD)这里,说明sqlcmd以关闭引号标识符的方式执行。我已经更改了我们的脚本,包括-I开关,以查看是否可以解决我们的问题。

我的问题是:

1)SET QUOTED_IDENTIFIER ON语句只影响DDL CREATE PROCEDURE语句,还是也影响存储过程的执行?我的快速测试表明是后者。

2)由于此开关的默认值为ON,我假设设置我的sqlcmd查询的-I开关不会产生负面影响。就所有目的而言,我将假设它与复制脚本内容,然后将其粘贴到查询管理器中并点击执行相同。如果我对此有误,请纠正我。我们的简单部署脚本如下:

@echo off

SET dbodir=../Schema Objects/Schemas/dbo/Programmability/Stored Procedures/
SET tpmdir=../Schema Objects/Schemas/TPM/Programmability/Stored Procedures/

echo --- Starting dbo schema

for %%f in ("%dbodir%*.sql") do (echo Running %%f.... && @sqlcmd -I -U %1 -P %2 -S %3 -d %4 -i "%dbodir%%%f")

echo --- Completed dbo schema

echo --- Starting TPM schema

for %%g in ("%tpmdir%*.sql") do (echo Running %%g.... && @sqlcmd -I -U %1 -P %2 -S %3 -d %4 -i "%tpmdir%%%g")

echo --- Completed TPM schema

pause

提前感谢。

编辑:

似乎还有一些进一步的信息需要确定存储过程的SET选项存储在哪里,而对此的接受答案提供了一些关于通用优先顺序规则的细节,适用于SET选项。对此的评论也指出:;

"...只有QUOTED_IDENTIFER和ANSI_NULLS设置在过程创建时被捕获。""...SET QUOTED IDENTIFIER 不能在存储过程内运行时设置"(我强调)。

我觉得这回答了我的第一个问题。

谁能接下来回答第二个问题呢?

4个回答

19

我将在此发布一些关于 QUOTED_IDENTIFIER 的理解。

简短版本

ANSI 要求标识符使用引号(而非字符串)。SQL Server 支持两种方式:

SQL Server 最初:

  • SELECT "Hello, world!" -- 引号
  • SELECT 'Hello, world!' -- 单引号
  • CREATE TABLE [The world's most awful table name] ([Hello, world!] int)
  • SELECT [Hello, world!] FROM [The world's most awful table name]

ANSI(即SET QUOTED_IDENTIFIER ON):

  • SELECT "Hello, world!" -- ANSI 不再支持字符串中的引号
  • SELECT 'Hello, world!' -- 单引号
  • CREATE TABLE "The world's most awful table name" ("Hello, world!" int)
  • SELECT "Hello, world!" FROM "The world's most awful table name"

详细版本

最初,SQL Server 允许您在字符串周围交替使用引号"...")和单引号'...')(就像 Javascript 一样):

  • SELECT "Hello, world!" -- 引号
  • SELECT 'Hello, world!' -- 单引号

如果您想要一个带有某些违反对象命名规则的名称的表、视图、过程、列等,可以将其包装在方括号中([]):

CREATE TABLE [The world's most awful table name] ([Hello, world!] int)
SELECT [Hello, world!] FROM [The world's most awful table name]

然后所有的都运作良好且有意义。

接着 ANSI 来了

然后 ANSI 出现了,他们有其他的想法:

  • 如果你有一个奇怪的名称,请用引号 ("...") 将其括起来
  • 使用撇号 ('...') 表示字符串
  • 而我们甚至不关心你的方括号

这意味着如果您想要“引用”一个奇怪的列或表名,您必须使用引号:

SELECT "Hello, world!" FROM "The world's most awful table name"

如果你了解SQL Server,你就知道引号已经被用来表示字符串。如果你盲目尝试执行那个ANSI-SQL,仿佛它是T-SQL:这是无意义的,而SQL Server会告诉你:

Msg 102, Level 15, State 1, Line 8
Incorrect syntax near 'The world's most awful table name'.

这就相当于试图执行道德上的等价物:

SELECT 'Hello, world!' FROM 'The world''s most awful table name'

这就像执行:

SELECT 'string' FROM 'string'

您必须选择使用新的 ANSI 行为

因此,微软添加了一个功能,让您选择使用 ANSI 版本的 SQL。

原始设置 (或QUOTED_IDENTIFIER关闭):

SELECT "Hello, world!" --valid
SELECT 'Hello, world!' --valid

SET QUOTED_IDENTIFIER ON:


在SQL Server中,SET QUOTED_IDENTIFIER ON指令用于设置标识符引号的使用方式。当该指令开启时,双引号将被视为标识符的一部分,而非字符串的标记符。
SELECT "Hello, world!" --INVALID
SELECT 'Hello, world!' --valid

SQL Server仍然允许您使用[方括号]而不是强制使用"引号", 但在设置QUOTED_IDENTIFIER为ON时,您不能使用"双引号包围字符串",您只能使用'单引号'


使用 SELECT "Hello, world!" 来说明列名和字符串字面量是很混淆的。最好将 SELECT "Hello, world!" FROM "The world's most awful table name" 更改为 SELECT "The world's most awful column name" FROM "The world's most awful table name" - Caltor
混淆是完美的;SQL Server 如何知道“Hello, world!”是指列名还是字符串。 - Ian Boyd
现在我很困惑!我原以为有些示例是字符串文字,但现在你把它们全部重命名成列名了! - Caltor
@Caltor 那就是你想要的!我会把它放回去! - Ian Boyd
我只是希望您在列名和FROM子句中更改它,而不是在最后4个示例中实际上是字符串文字的情况下更改它。 - Caltor
显示剩余2条评论

3

我将以下命令保存到文本文件中,然后使用SQLCMD执行:

SET QUOTED_IDENTIFIER ON
SET QUOTED_IDENTIFIER OFF

在SQL Profiler中检查,SQLCMD -i <filename> 在我的系统上使用以下连接选项进行连接:

-- network protocol: LPC
set quoted_identifier on
...

然而,在连接时,SQLCMD发出以下命令:
SET QUOTED_IDENTIFIER OFF SET TEXTSIZE 4096

然后运行我的脚本。

所以,对于问题2的答案是否定的 - 使用SQLCMD -i运行脚本与从SSMS执行(使用默认连接选项)不同。如果脚本需要QUOTED_IDENTIFIER ON,则需要在开始时显式设置它,如果您要以这种方式执行它。


2
嗨,艾德。我认为您使用了正确的开关,但是选择不正确。我指的是大写字母I。小写字母-i指定文件名,大写字母-I则指引号标识符。http://msdn.microsoft.com/en-us/library/ms162773.aspx。我的主要问题是,使用-I开关是否有效地意味着与通过管理工作室运行命令相同?不过使用分析器的想法很好。我可能会进一步调查那个。 - Mr Moose
2
@Mr Moose - 我的错误。在这种情况下,是的,这应该与通过SSMS执行相同。 - Ed Harper

0
SET QUOTED_IDENTIFIER ON

GO

SET ANSI_NULLS ON

GO
--SQL PROCEDURE, SQL FUNCTIONS, SQL OBJECTGO

SET QUOTED_IDENTIFIER OFF

GO

SET ANSI_NULLS ON

GO

ANSI NULL ON/OFF:

此选项指定 ANSI NULL 比较的设置。当此选项打开时,任何将值与 null 进行比较的查询都会返回 0。当关闭时,任何将值与 null 进行比较的查询都会返回 null 值。

QUOTED IDENTIFIER ON/OFF:

此选项指定双引号的使用设置。当此选项开启时,双引号将作为 SQL Server 标识符(对象名称)的一部分使用。在标识符也是 SQL Server 保留字的情况下,这可能非常有用。


0
关于您的问题#1,原因在{{link1:使用SET语句时的注意事项}}中已经说明。它指出:

存储过程在执行时使用指定的SET设置,除了SET ANSI_NULLS和SET QUOTED_IDENTIFIER。指定SET ANSI_NULLS或SET QUOTED_IDENTIFIER的存储过程使用在存储过程创建时指定的设置。如果在存储过程内部使用,任何SET设置都将被忽略。


1
谢谢。我在最初提问后偶然发现了相同的信息,因此进行了编辑更新。 - Mr Moose

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接