你不能做你想做的事。不是通过使用游标迭代表中的行并将定义拉出到变量中以执行和更改ansi_nulls
并quoted_identifier
根据每一行给出的值,因为游标执行必须在一批中进行。
为什么“必须住在一批”很重要?继续阅读。
set quoted_identifier on;
select @@options & 256; -- will print 256 if qi is on, 0 if it is off
这将打印256
. 到现在为止还挺好。但是这个呢?
set quoted_identifier on;
print @@options & 256;
set quoted_identifier off;
print @@options & 256;
这会打印256
然后0
吗?没有。它打印0
,然后0
再打印。奇怪的!好的,让我们确保在自己的批处理quoted_identifier
中on
运行它,然后尝试有条件地关闭它:
set quoted_identifier on;
go -- note this additional go
if (1 = 0) set quoted_identifier off;
print @@options & 256;
当我们开始第二批(之后的位go
)时,quoted_identifier
设置肯定是打开的。然后我们只关闭它if 1 = 0
。由于 1 不等于 0,quoted_identifier
因此应保持打开状态。所以我们希望打印256
.
我们实际打印什么?0
. 这是怎么回事?让我们检查一下文档:
对于顶级 ad-hoc 批处理解析开始使用会话的当前设置 QUOTED_IDENTIFIER。在解析批处理时,任何出现的 SET QUOTED_IDENTIFIER 都会从那时起更改解析行为,并为会话保存该设置。所以批处理解析并执行后,会话的QUOTED_IDENTIFER设置将根据批处理中最后一次出现的SET QUOTED_IDENTIFIER进行设置。
(重点补充)
您不能有条件地更改quoted_identifier
批次中的设置。似乎动态 SQL 也无法拯救你,因为你需要set quoted_identifier
然后create procedure
在同一个动态 sql 字符串中,而你不能,因为一个动态exec
是一个批处理,并且create procedure
必须是批处理中的第一个语句。
但是等等,还有更多。
declare @cmdOn varchar(max) = 'exec(''set quoted_identifier on; print @@options & 256;'')';
declare @cmdOff varchar(max) = 'exec(''set quoted_identifier off; print @@options & 256;'');';
declare @both varchar(max) = concat(@cmdOn, char(10), @cmdOff);
print @both;
exec (@both);
只是为了清楚发生了什么,这是它的输出:
exec('设置quoted_identifier on;打印@@options & 256;')
exec('设置quoted_identifier off;打印@@options & 256;');
256
0
所以……耶!我们已经成功地执行了一条动态 sql(的值@both
),并且我们改变了quoted_identifier
里面的设置!凉爽的。我们付出了什么代价?嵌套动态调用exec()
.
这能救我们吗?没有。
set quoted_identifier on; -- this is the only line that matters;
declare @qi varchar(max) = 'exec(''set quoted_identifier off;'')'; -- it makes no difference what you put here
declare @def varchar(max) = 'exec(''create or alter procedure p as begin set nocount on end;'');';
declare @both varchar(max) = concat(@qi, char(10), @def);
exec (@both);
select uses_quoted_identifier from sys.sql_modules where object_name(object_id) = 'p';
-- returns 1
嵌套调用对exec
我们没有帮助,因为(来自文档):
对于使用 sp_executesql 或 exec() 的嵌套批处理,解析开始使用会话的 QUOTED_IDENTIFIER 设置。如果嵌套批处理位于存储过程中,则使用存储过程的 QUOTED_IDENTIFIER 设置开始解析。在解析嵌套批处理时,任何出现的 SET QUOTED_IDENTIFIER 都会从那时起更改解析行为,但不会更新会话的 QUOTED_IDENTIFIER 设置。
你能做些什么呢?
在过程创建批处理之外运行您的“光标” 。
例如,编写一个小程序(或 powershell 脚本)来读取备份定义以及所需的设置,然后将每个设置create procedure
作为自己的命令执行。
或者读取备份的内容并将其全部转储到一个文件中,包括备份表中每一行的“go”语句。将文件内容作为脚本执行。
基于程序的解决方案的伪代码:
通过 ssms/whatever 备份 sql_modules
通过 ssms/whatever 删除程序
运行程序
打开与 sql 的连接
从备份表中读取定义、设置
foreach(定义,设置)
执行 sql 命令 ("set ansi_nulls ?; setquoted_identifier ?");
执行sql命令(定义)
紧密连接
退出程序