一个脚本中的多组 Quoted_identifier 取最后一个?

Joh*_*hnG 4 sql-server t-sql error-handling sql-server-2019

我正在使用 SQL Server 2019 并发现了一个奇怪的行为。研究并没有让我走得更远。

有人可以解释这种行为吗?

SET QUOTED_IDENTIFIER ON; 
if ((256 & @@options) = 256) print '1- quoted_identifier is on' else print '1- quoted_identifier is off';
BEGIN TRY 
    if ((256 & @@options) = 256) print '2- quoted_identifier is on' else print '2- quoted_identifier is off';
END TRY 
BEGIN CATCH
    if ((256 & @@options) = 256) print '3- quoted_identifier is on' else print '3- quoted_identifier is off';
    -- SET QUOTED_IDENTIFIER OFF
    -- if ((256 & @@options) = 256) print '4- quoted_identifier is on' else print '4- quoted_identifier is off';
END CATCH
Run Code Online (Sandbox Code Playgroud)

返回:

1- quoted_identifier is on
2- quoted_identifier is on
Run Code Online (Sandbox Code Playgroud)

但以下代码:

SET QUOTED_IDENTIFIER ON; 
if ((256 & @@options) = 256) print '1- quoted_identifier is on' else print '1- quoted_identifier is off';
BEGIN TRY 
    if ((256 & @@options) = 256) print '2- quoted_identifier is on' else print '2- quoted_identifier is off';
END TRY 
BEGIN CATCH
    if ((256 & @@options) = 256) print '3- quoted_identifier is on' else print '3- quoted_identifier is off';
    SET QUOTED_IDENTIFIER OFF
    if ((256 & @@options) = 256) print '4- quoted_identifier is on' else print '4- quoted_identifier is off';
END CATCH
Run Code Online (Sandbox Code Playgroud)

返回:

1- quoted_identifier is off
2- quoted_identifier is off
Run Code Online (Sandbox Code Playgroud)

即使它没有进入捕获!我肯定错过了什么。

我什至能够将代码简化为最简单的:

SET QUOTED_IDENTIFIER ON; 
if ((256 & @@options) = 256) print '1- quoted_identifier is on' else print '1- quoted_identifier is off';
SET QUOTED_IDENTIFIER OFF
if ((256 & @@options) = 256) print '2- quoted_identifier is on' else print '2- quoted_identifier is off';
Run Code Online (Sandbox Code Playgroud)

结果:

1- quoted_identifier is off
2- quoted_identifier is off
Run Code Online (Sandbox Code Playgroud)

我有一些使用 的代码FOR XML,这要求我将引用的标识符设置为 ON,但我需要将其重新设置为 OFF,无论 XML 部分是成功还是失败。你会怎么做?

我的测试表明,如果我将SET QUOTED_IDENTIFIERto off 放在 the 中CATCH,则插入无法说明我引用的标识符设置不正确,尽管它设置在TRYto的开头ON

Cha*_*ace 7

这是设计使然:

对于顶级临时批处理解析开始使用会话的当前设置QUOTED_IDENTIFIER。当批处理被解析时,任何发生都SET QUOTED_IDENTIFIER将改变从那时起的解析行为,并为会话保存该设置。所以在批处理被解析和执行后,会话的QUOTED_IDENTIFER设置将根据SET QUOTED_IDENTIFIER批处理中的最后一次出现来设置。

因此,的行为QUOTED_IDENTIFIER取决于批处理的解析,而不是执行。解析批处理开始时的默认值来自当前连接设置。

@@OPTIONS只显示当前的默认值,如果批处理被解析将使用。这就是为什么 SSMS 总是放在SET一个单独的批次中。

的执行SET与 的行为无关@@OPTIONS,除了更改以后批次的默认值。


您可以在 db-fiddle 中看到这一点

set QUOTED_IDENTIFIER  on;
create table "select"(i int);
drop table "select";
GO

set QUOTED_IDENTIFIER  off;
create table "select"(i int);
drop table "select";
--  Incorrect syntax near 'select'.
GO

set QUOTED_IDENTIFIER  off;
if (1=0)
begin
    set QUOTED_IDENTIFIER  on;
end
create table "select"(i int);
drop table "select";
GO

set QUOTED_IDENTIFIER  on;
if (1=0)
begin
    set QUOTED_IDENTIFIER  off;
end
create table "select"(i int);
drop table "select";
-- Incorrect syntax near 'select'.
GO
Run Code Online (Sandbox Code Playgroud)


Ron*_*ldo 1

Charlieface的答案似乎已经适当地解释了您在使用时描述的行为的原因SET QUOTED_IDENTIFIER,因此,如果您需要在 SP 内使用它,我将留下一个可以使用的方法:

建议的解决方案

QUOTED_IDENTIFIER将批处理的一部分隔离到另一个过程中,并从需要OFF这样的过程中调用它: db<>fiddle - 在您的计算机上运行 2 个不同的会话,因为我无法使其在 dbfiddle 上正常运行。

  • 它告诉你的事情与你的想法略有不同。它不会告诉您“QUOTED_IDENTIFIER”在*解析*阶段是否打开/关闭,它只告诉您连接上的当前默认值(如果您在没有任何“SET”的情况下执行*新*批处理,什么设置将生效`)。我认为除了做明显的事情并寻找“SET”语句(记住它们忽略“IF”块)之外,没有任何方法可以显示当前的解析时间设置。 (2认同)