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