TSQL - 如何在BEGIN .. END块中使用GO?

Blu*_*eft 89 sql t-sql sql-server sql-server-2008

我正在生成一个脚本,用于自动将更改从多个开发数据库迁移到暂存/生产.基本上,它需要一堆更改脚本,并将它们合并到一个脚本中,将每个脚本包装在一个IF whatever BEGIN ... END语句中.

但是,某些脚本需要一个GO语句,以便例如SQL解析器在创建新列后知道它.

ALTER TABLE dbo.EMPLOYEE 
ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
GO -- Necessary, or next line will generate "Unknown column:  EMP_IS_ADMIN"
UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever
Run Code Online (Sandbox Code Playgroud)

但是,一旦我将其包装在一个IF块中:

IF whatever
BEGIN
    ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
    GO
    UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever
END
Run Code Online (Sandbox Code Playgroud)

它失败了,因为我发送了一个BEGIN没有匹配的END.但是,如果我删除GO它,它会再次抱怨一个未知的列.

有没有办法在单个IF块中创建和更新同一列?

Ode*_*ded 41

GO 不是SQL - 它只是一些MS SQL工具中使用的批处理分隔符.

如果不使用它,则需要确保语句单独执行 - 不同批次或使用动态SQL进行填充(感谢@gbn):

IF whatever
BEGIN
    ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL;

    EXEC ('UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever')
END
Run Code Online (Sandbox Code Playgroud)

  • 是的,我理解.这不回答问题 - 我需要在同一个`IF`块中创建和更新列. (5认同)

Min*_*cob 36

我有同样的问题,最后设法使用SET NOEXEC解决它.

IF not whatever
BEGIN
    SET NOEXEC ON; 
END

ALTER TABLE dbo.EMPLOYEE ADD COLUMN EMP_IS_ADMIN BIT NOT NULL
GO
UPDATE dbo.EMPLOYEE SET EMP_IS_ADMIN = whatever

SET NOEXEC OFF; 
Run Code Online (Sandbox Code Playgroud)

  • 这是一个很好的解决方案! (2认同)
  • +1!这是迄今为止唯一的*实用*答案,可用于 SS `SQLCMD` 模式脚本(即主部署脚本),该脚本调用(通过 `:r` 命令)其他 SS 脚本(即子部署脚本),其中包含一些`if` 语句中的那些调用。Oded、mellamokb 和 Andy Joiner 将 *all* 包含在 `exec` 调用/`begin`-`end` 中的那些语句的答案是非启动的。此外,如果有一个 `create` 语句(例如,在它之前需要一个显式的 `go`),`begin`-`end` 方法将不起作用。但是,伙计,“神圣的双重否定,蝙蝠侠!” ;) (2认同)
  • 为了提高可读性(例如,为了帮助克服双重否定并更清楚地表明它正在模拟*虚拟* `if` 块),我会在块前面加上 `-- If Whatever` 注释,缩进块并后缀带有“--end Ifwhatever”注释的块。 (2认同)

mel*_*okb 16

您可以尝试sp_executesql将每个GO语句之间的内容拆分为要执行的单独字符串,如下例所示.此外,还有一个@statementNo变量来跟踪正在执行哪个语句,以便在出现异常时进行简单调试.行号将相对于导致错误的相关语句编号的开头.

BEGIN TRAN

DECLARE @statementNo INT
BEGIN TRY
    IF 1=1
    BEGIN
        SET @statementNo = 1
        EXEC sp_executesql
            N'  ALTER TABLE dbo.EMPLOYEE
                    ADD COLUMN EMP_IS_ADMIN BIT NOT NULL'

        SET @statementNo = 2
        EXEC sp_executesql
            N'  UPDATE dbo.EMPLOYEE
                    SET EMP_IS_ADMIN = 1'

        SET @statementNo = 3
        EXEC sp_executesql
            N'  UPDATE dbo.EMPLOYEE
                    SET EMP_IS_ADMIN = 1x'
    END
END TRY
BEGIN CATCH
    PRINT 'Error occurred on line ' + cast(ERROR_LINE() as varchar(10)) 
       + ' of ' + 'statement # ' + cast(@statementNo as varchar(10)) 
       + ': ' + ERROR_MESSAGE()
    -- error occurred, so rollback the transaction
    ROLLBACK
END CATCH
-- if we were successful, we should still have a transaction, so commit it
IF @@TRANCOUNT > 0
    COMMIT
Run Code Online (Sandbox Code Playgroud)

您也可以通过简单地将它们包装在单引号(')中来轻松执行多行语句,如上例所示.''在生成脚本时,不要忘记使用双引号()来转义字符串中包含的任何单引号.


Blu*_*eft 9

我最终通过替换GO自己的每个实例来实现它

END
GO

---Automatic replacement of GO keyword, need to recheck IF conditional:
IF whatever
BEGIN
Run Code Online (Sandbox Code Playgroud)

这非常适合将每组语句包装在一个字符串中,但仍远非理想.如果有人找到更好的解决方案,发布它,我会接受它.

  • 如果第一个条件是"如果此列不存在",则块中的第一个语句是"添加此列",然后条件的第二个检查将找到该列而不执行第二个语句, (4认同)

And*_*ner 7

您可以将语句括在BEGIN和END中,而不是GO中

IF COL_LENGTH('Employees','EMP_IS_ADMIN') IS NULL --Column does not exist
BEGIN
    BEGIN
        ALTER TABLE dbo.Employees ADD EMP_IS_ADMIN BIT
    END

    BEGIN
        UPDATE EMPLOYEES SET EMP_IS_ADMIN = 0
    END
END
Run Code Online (Sandbox Code Playgroud)

(在Northwind数据库上测试)

编辑:(可能在SQL2012上测试过)

  • 使用SQL Server 2008 R2,这似乎对我不起作用,我仍然收到错误'无效列名'EMP_IS_ADMIN'.' 在UPDATE行. (9认同)