ПРИМЕЧАНИЕ ДЛЯ ЧИТАТЕЛЕЙ: Пожалуйста, прочитайте весь вопрос, включая пример кода (т.е. не только заголовок). Этот вопрос не о том, как лучше прокрутить базы данных, и не о том, почему [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 выполняет несколько шагов при выполнении пакета запроса:
- Анализировать
- Compile
- Фактическое исполнение
Важно понимать, что это отдельные этапы, которые могут вызвать ошибки перед переходом к следующему этапу (и, следовательно, отменить дальнейшую обработку перед переходом к следующему этапу). В данном случае ошибка происходит в Шаге 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»ошибка не произойдет.