Как отключить цитируемый идентификатор при создании хранимой процедуры с помощью exec
У меня есть сценарий для программного изменения столбцов нескольких типов таблиц, определенных пользователем. Для этого мне нужно отбросить указанные хранимые процедуры. Итак, я разработал свои сценарии SQL для выполнения следующих действий:
- Создание резервной копии хранимых процедур из таблицы sys.sql_modules в промежуточную таблицу (определение, столбцы uses_ansi_nulls и uses_quoted_identifier).
- Удаление хранимой процедуры.
- Изменение типов таблиц, определенных пользователем.
- Теперь я использую столбец определения промежуточной таблицы для воссоздания хранимой процедуры, но я не могу использовать значения столбцов uses_ansi_nulls и uses_quoted_identifier. Как я могу повторно использовать столбцы uses_ansi_nulls и uses_quoted_identifier при воссоздании хранимой процедуры.
Я использую курсор, чтобы преобразовать содержимое промежуточных столбцов в переменные, а затем с помощью exec() выполняю определение хранимой процедуры, как показано ниже:
SET @ProcDefinition_Complete=@uses_ansi_nulls_Definition + CHAR(10)+' GO '+ CHAR(10)+@uses_quoted_identifier_Definition+ CHAR(10)+' GO '+ CHAR(10)+ @ProcDefinition
EXEC (@ReferencingDefinition_Complete)
Вышеуказанное утверждение дает ошибку:
Incorrect syntax near 'GO'.
И когда я удаляю оператор GO, он выдает ошибку:
'CREATE/ALTER PROCEDURE' must be the first statement in a query batch.
2 ответа
Вы не можете делать то, что пытаетесь сделать. Не путем итерации по строкам в таблице с помощью курсора и извлечения определений в переменные для выполнения и изменения
ansi_nulls
и в соответствии со значениями, указанными в каждой строке, потому что выполнение курсора должно выполняться в одном пакете.
Почему важно «жить одной партией»? Читать дальше.
set quoted_identifier on;
select @@options & 256; -- will print 256 if qi is on, 0 if it is off
Это напечатает. Все идет нормально. Но как насчет этого?
set quoted_identifier on;
print @@options & 256;
set quoted_identifier off;
print @@options & 256;
Разве эта печать а потом? Неа. он печатает, и снова. Странный! ОК, давай убедимся
on
запустив его в отдельном пакете, а затем попробуйте условно отключить его:
set quoted_identifier on;
go -- note this additional go
if (1 = 0) set quoted_identifier off;
print @@options & 256;
Когда мы запускаем вторую партию (бит после
go
) настройки наверняка включены. Тогда мы только выключаем его
if 1 = 0
. Поскольку 1 не равно 0, должен оставаться включенным. Итак, мы ожидаем напечатать
256
.
Что мы на самом деле печатаем?
0
. В чем дело? Давайте проверим документы:
Для специального пакетного анализа верхнего уровня начинается с использования текущего параметра сеанса для QUOTED_IDENTIFIER. Когда пакет анализируется, любое появление SET QUOTED_IDENTIFIER изменит поведение синтаксического анализа с этого момента и сохранит этот параметр для сеанса. Таким образом, после анализа и выполнения пакета параметр сеанса QUOTED_IDENTIFER будет установлен в соответствии с последним вхождением SET QUOTED_IDENTIFIER в пакете.
(Курсив добавлен)
Вы не можете условно изменить настройку в пакете. Похоже, что динамический SQL тоже не может спасти вас, потому что вам нужно
set quoted_identifier
а затем в той же динамической строке sql, и вы не можете, потому что одна динамика - это один пакет, и он должен быть первым оператором в пакете.
Но подождите, это еще не все.
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 ('установить цитируемый_идентификатор; print @@ options & 256;') exec ('установить значение quoted_identifier off; print @@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» для каждой строки в таблице резервных копий. Выполните содержимое файла как сценарий.
Определите настройку вне динамического оператора. Если вы измените параметр внешней области, например, область динамического оператора унаследует и этот параметр.
Например:
SET ANSI_NULLS OFF;
SELECT @@OPTIONS & 32; --Returns 0
EXEC sys.sp_executesql N'SELECT @@OPTIONS & 32;'; --Returns 0
GO
SET ANSI_NULLS ON;
SELECT @@OPTIONS & 32; --Returns 32
EXEC sys.sp_executesql N'SELECT @@OPTIONS & 32;'; --Returns 32
Поскольку последние 2 утверждения возвращаются
32
это означает, что
ANSI_NULLS
включен в обоих операторах. Так же,
0
означает, что он отключен в предыдущих утверждениях.
Обратите внимание, что если вы измените параметр во внутренней (динамической) области, это изменение параметра не распространяется на внешнюю область:
SELECT @@OPTIONS & 32; --returns 32
EXEC sys.sp_executesql N'SET ANSI_NULLS OFF; SELECT @@OPTIONS & 32;'; --returns 0
SELECT @@OPTIONS & 32; --Returns 32
Что касается причин возникновения ошибок, это потому, что это не оператор T-SQL, и поэтому он не распознается.
GO
является служебным заявлением и распознается IDE и интерфейсами командной строки (такими как SSMS, ADS и
sqlcmd
) в качестве разделителя партий. Он не распознается компилятором T-SQL, поэтому его не следует включать в динамические операторы.