Оператор IF не пропускает TempDB при циклическом просмотре баз данных с помощью sp_MSForEachDB

8

[SQL Server 2012 SP2 EE]

Почему следующий скрипт выдает ошибку, связанную с tempdb?

    exec sp_MSForEachDB '
    IF ( (select database_id from sys.databases where name = ''?'') > 4)
    BEGIN 
    ALTER AUTHORIZATION ON DATABASE::? TO [sa];
    ALTER DATABASE [?] SET RECOVERY SIMPLE;
    END'

Вот ошибка, которую я получаю:

  Msg 5058, Level 16, State 1, Line 5
  Option 'RECOVERY' cannot be set in database 'tempdb'.

Это делает работу, которую он должен сделать. Но я не могу придумать причину ошибки. Я знаю, что databaseID базы данных tempdb равен 2, тогда, по крайней мере, он не должен пытаться установить параметр для базы данных tempdb.

GaganLamba
источник

Ответы:

4

ПРИМЕЧАНИЕ ДЛЯ ЧИТАТЕЛЕЙ: Пожалуйста, прочитайте весь вопрос, включая пример кода (т.е. не только заголовок). Этот вопрос не о том, как лучше прокрутить базы данных, и не о том, почему [tempdb] получает эту ошибку. OP уже пытается избежать выполнения ALTERоператоров во всех системных базах данных (ну, в 4 видимых) и спрашивает, почему оператор IF, который должен пропускать [tempdb], по-видимому, не пропускает его.

Почему следующий скрипт выдает ошибку, связанную с tempdb?

Причина в том, что IFоператор влияет только на то, что происходит, когда код фактически выполняется, но SQL Server все еще должен анализировать и компилировать пакет перед его выполнением. Ошибки синтаксического анализа связаны с синтаксисом, например, с обеспечением правильности формирования операторов SQL и правильного объявления переменных:

-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
SELECT @Bob;

получает следующую ошибку:

Msg 137, Level 15, State 2, Line 2
Must declare the scalar variable "@Bob".

И следующее:

-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
CREATE TABLE b

получает следующую ошибку:

Msg 102, Level 15, State 1, Line 1
Incorrect syntax near 'b'.

Если пакет успешно анализируется, он компилируется, и в это время проверяются такие вещи, как разрешения, и выполняются некоторые другие проверки.

-- parse the following by hitting Control-F5 or clicking the check mark in SSMS
IF (1 = 0)
BEGIN 
  ALTER AUTHORIZATION ON DATABASE::[tempdb] TO [sa];
  ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
END;

Вышеуказанный SQL сформирован правильно, поэтому пакет успешно анализируется. Теперь попробуйте выполнить вышеупомянутый SQL, нажав F5 или Control-E или ! Кнопка Выполнить и т. Д.

На этот раз вы получите следующую ошибку:

Msg 5058, Level 16, State 1, Line 4
Option 'RECOVERY' cannot be set in database 'tempdb'.

хотя он IF (1 = 0)гарантирует, что код никогда не будет запущен. Это означает, что вы столкнулись с ошибкой компиляции. Вы можете обойти эти типы ошибок, перемещая нарушающий код в подпроцесс посредством EXECвызова.

Выполните следующее, и оно будет успешно завершено, так как то, что находится внутри EXEC(), не анализируется и не компилируется, пока этот оператор не будет выполнен во время выполнения.

IF (1 = 0)
BEGIN
  EXEC('
    ALTER AUTHORIZATION ON DATABASE::[tempdb] TO [sa];
    ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
  ');
END;

Подводя итог: во-
первых, учтите, что sp_MSForEachDBциклически проходит через базы данных и выполняет ваш переданный SQL после замены на ?имя текущей базы данных. Таким образом, когда курсор внутри sp_MSForEachDBполучает [tempdb], он фактически делает следующее:

IF ( (select database_id from sys.databases where name = ''tempdb'') > 4)
BEGIN 
  ALTER AUTHORIZATION ON DATABASE::tempdb TO [sa];
  ALTER DATABASE [tempdb] SET RECOVERY SIMPLE;
END

Во-вторых, SQL Server выполняет несколько шагов при выполнении пакета запроса:

  1. Анализировать
  2. Compile
  3. Фактическое исполнение

Важно понимать, что это отдельные этапы, которые могут вызвать ошибки перед переходом к следующему этапу (и, следовательно, отменить дальнейшую обработку перед переходом к следующему этапу). В данном случае ошибка происходит в Шаге 2 - Компиляция - как доказано во втором и последнем примере выше (первый, с которого нужно начинать IF (1 = 0)). Функция IF (1 = 0)предотвращает BEGIN...ENDзапуск кода внутри блока, но ошибка все равно возникает. Следовательно, ошибка не происходит из-за фактической попытки выполнить два ALTERоператора.

Причина того, что перенос ALTERоператоров внутри EXEC()функции работает, заключается в том, что SQL Server не будет анализировать и компилировать то, что находится внутри, до тех EXEC()пор, пока он не EXEC()будет запущен. В этот момент IF ( (select database_id from sys.databases where name = ''?'') > 4)оператор будет иметь возможность работать , так как партия не будет терпеть неудачу во время компиляции и сделает его исполнение, и IFоператор будет пропускать [tempdb]и «Option„RECOVERY“не может быть установлена в базе данных" данных TempDb»ошибка не произойдет.

Соломон Руцкий
источник
У меня вопрос: почему оператор if разрешает отправку идентификатора tempdb (то есть 2) в блок операторов if, если он не удовлетворяет условию
превышения
1
@GaganLamba Я понимаю ваш вопрос и ответил на него очень конкретно. Вы запускали мои примеры? Как я сказал в ответе, это ошибка времени компиляции, а не времени выполнения. Следовательно, ни IFоператор, ни любые другие операторы SQL (включая два ALTER) не выполняются в этот момент. IFЗаявление не позволяет ID TempDb, чтобы быть направлены на то , что находится внутри BEGIN/ ENDблока, и код даже не запустив ALTERзаявления на данный момент. Ошибка генерируется SQL Server, поскольку он проверяет SQL перед его выполнением. Я добавил сводный раздел до конца.
Соломон Руцкий
3

Сообщение 5058, уровень 16, состояние 1, строка 5 Параметр «ВОССТАНОВЛЕНИЕ» не может быть установлен в базе данных «tempdb».

Это делает работу, которую он должен сделать. Но я не могу придумать причину ошибки.

Прежде всего, нет необходимости использовать ms_foreachdbего недокументированный, и вы можете зацикливаться на нем, используя простой курсор. Что касается ошибки, вы пытаетесь изменить модель восстановления всей базы данных, including tempdbно вы не можете изменить модель восстановления базы данных tempdb, а также не можете выполнить какую-либо операцию резервного копирования, поэтому вы получили это сообщение об ошибке. Это не разрешено Microsoft. Пожалуйста, прочитайте больше об операциях, которые вы можете выполнять на базе данных tempdb.

Shanky
источник
1
Я понимаю, что ms_foreachdb недокументирован, и я не должен его использовать. Я также понимаю, что база данных tempdb не предназначена для резервного копирования или изменения параметров восстановления. Но мой вопрос все еще остается без ответа относительно того, почему сервер SQL пытается изменить параметр для TEMPDB? Идентификатор TEMPDB, равный 2, даже не был возвращен.
GaganLamba
1
@GaganLamba SQL Server фактически не пытается изменить параметр для TEMPDB. Эти ALTERзаявления просто быть проверены , не выполняются. Я предоставляю подробности в моем ответе .
Соломон Руцкий
3

Помимо того, что вы не можете изменить параметр восстановления для базы данных tempdb, вам не нужен цикл для того, что вы делаете:

Запустите в SSMS, нажав CTRL+T

select 'alter authorization on database::' + quotename(name) + ' to [sa];' + char(10) + 'alter database ' + quotename(name) + ' set recovery simple;'
from sys.databases
where database_id > 4
    and state_desc = 'ONLINE'
Кин Шах
источник