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

时间:2011-06-16 18:36:48

标签: sql sql-server tsql 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

然而,一旦我将其包裹在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

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

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

8 个答案:

答案 0 :(得分:39)

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

答案 1 :(得分:32)

我遇到了同样的问题,最后设法使用 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; 

答案 2 :(得分: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

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

答案 3 :(得分:8)

我最终通过用

替换自己行中GO的每个实例来实现它
END
GO

---Automatic replacement of GO keyword, need to recheck IF conditional:
IF whatever
BEGIN

这比将每组语句包装在一个字符串中更好,但仍然。如果有人找到更好的解决方案,请发布它,我会接受它。

答案 4 :(得分: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

(在Northwind数据库上测试)

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

答案 5 :(得分:0)

我过去曾为此使用RAISERROR

IF NOT whatever BEGIN
    RAISERROR('YOU''RE ALL SET, and sorry for the error!', 20, -1) WITH LOG
END

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

答案 6 :(得分:0)

您可以尝试以下解决方案:

if exists(
SELECT...
)
BEGIN
PRINT 'NOT RUN'
RETURN
END

--if upper code not true

ALTER...
GO
UPDATE...
GO

答案 7 :(得分:-1)

您可以合并GOTOLABEL语句来跳过代码,从而保留GO个关键字。