在使用 sqlcmd 调用的脚本中使用 SET NOEXEC ON 时出现意外行为

unexpected behavior when using SET NOEXEC ON in a script called using sqlcmd

下面是一组简化脚本的示例,它准确地再现了为 Prod 编写的更复杂脚本中存在的问题。

simulation.batsandbox 数据库存在之前 运行 时,它工作正常 - 数据库与填充的 table 和一个视图一起创建。这是终端输出 -

但是,在初始执行批处理文件后,即使在 if 块中使用了 set NOEXEC on;,后续执行也会导致出现数据库错误消息。它似乎在视图创建时窒息,因为 table 不存在。虽然 table 不存在是有道理的,但为什么在 set NOEXEC on 已设置时它仍然试图创建视图?如果数据库已经存在,如何修改逻辑以确保它不会尝试创建视图?

4 个文件 -
simulation.bat

@echo off
sqlcmd -S TheServerName -E -d master -i .\Simulation.sql -v db_name=sandbox
pause

Simulation.sql

print 'database name    $(db_name)';

--if database already exists then print a message and exit script, otherwise create database
if exists (select 1 from sys.databases where [name] = '$(db_name)')
begin
  print '--- Database $(db_name) already exists - script execution aborted ---';
    set NOEXEC on;  --prevent execution of statements in this batch and batches that follow it
end;

if not exists (select 1 from sys.databases where [name] = '$(db_name)')
begin
  create database $(db_name);
  print '--- $(db_name) database created ---';
end;
go

use $(db_name);
go

:r .\Tally.sql
go

print 'Table creation is complete.';
go

:r .\vw_TallyRows.sql
go

print 'View creation is complete.';
go

set NOEXEC off; --allow execution of statements that follow
go

print 'Reached end of script.';
go

Tally.sql

create table Tally (n int not null primary key);
insert into Tally values (1), (2), (3),  (4), (5), (6), (7), (8), (9), (10);

vw_TallyRows.sql

create view vw_TallyRows as
select rows = count(1) from dbo.Tally;

在某些时候,我突然想到,如果数据库存在(如第二张屏幕截图中打印到终端的行所示),那么 dbo.Tally table 也应该存在。基于此,出现无效的对象名称错误消息是没有意义的。根本原因:在 Simulation.sql use $(db_name); 之后 检查数据库是否存在的代码块

因此,解决方案是重新排列 Simulation.sql 脚本开头的语句顺序,并添加另一个存在性检查 -

print 'database name    $(db_name)';

if not exists (select 1 from sys.databases where [name] = '$(db_name)')
begin
  create database $(db_name);
  print '--- $(db_name) database created ---';
end;
go

use $(db_name);
go

if exists (select 1 from sys.databases where [name] = '$(db_name)') and exists (select distinct 1 from sys.all_objects where is_ms_shipped = 0)
begin
  print '--- Database $(db_name) already exists - script execution aborted ---';
    set NOEXEC on;  --prevent execution of statements in this batch and batches that follow it
end;
go

--no change to remaining logic in script

sandbox数据库还不存在时的终端输出:

sandbox 数据库存在时的终端输出: