GO causes error when used in EXEC: “Incorrect synt

2020-04-13 20:32发布

问题:

I've created this stored procedure which dynamically creates the same trigger for all my tables:

USE [MyDatabase]
GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

--Drop Stored Procedure
BEGIN TRY
     DROP PROCEDURE [dbo].[sp_CreateDataChangedTrigger]
END TRY
BEGIN CATCH
END CATCH
GO

--Create Stored Procedure

-- ================================================
-- Template generated from Template Explorer using:
-- Create Procedure (New Menu).SQL
--
-- Use the Specify Values for Template Parameters 
-- command (Ctrl-Shift-M) to fill in the parameter 
-- values below.
--
-- This block of comments will not be included in
-- the definition of the procedure.
-- ================================================

-- =============================================
-- Author:      Scott Bass
-- Create date: 06JUL2014
-- Description: Create Data Change triggers
-- =============================================
CREATE PROCEDURE sp_CreateDataChangedTrigger
    -- Add the parameters for the stored procedure here
    @TableName varchar(255), 
    @TableKey  varchar(255),
    @Debug     bit=1
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

     DECLARE @SQL varchar(max);

     SET @SQL = '
--Drop Trigger
BEGIN TRY
     DROP TRIGGER [dbo].[TR_' + @TableName + '_Audit]
END TRY
BEGIN CATCH
END CATCH
GO

--Create Trigger
CREATE TRIGGER [dbo].[TR_' + @TableName + '_Audit]
     ON [dbo].[' + @TableName + ']
     AFTER INSERT, UPDATE, DELETE
AS
BEGIN
     SET NOCOUNT ON
     DECLARE @event_type [char]

     --Get Event Type
     IF EXISTS(SELECT * FROM INSERTED)
     IF EXISTS(SELECT * FROM DELETED)
          SELECT @event_type = ''U''
     ELSE
          SELECT @event_type = ''I''
     ELSE
     IF EXISTS(SELECT * FROM deleted)
          SELECT @event_type = ''D''
     ELSE
     --no rows affected - cannot determine event
          SELECT @event_type = ''K''

     IF @event_type IN (''I'',''U'') BEGIN
          DECLARE @CurrentUserID INT;
          SELECT  @CurrentUserID = u.UserID
          FROM    [dbo].[dim_Users] u
          WHERE   u.[Username] = dbo.udfUserName()

          UPDATE  t
          SET     DateModified = GETDATE(),
                  WhoModifiedID = @CurrentUserID
          FROM    INSERTED e
          JOIN    [dbo].[' + @TableName + '] t ON e.[' + @TableKey + '] = t.[' + @TableKey + ']
     END

     IF @event_type = ''D'' BEGIN
          no_op:  --Nothing for now
     END
END
GO
';

     IF @Debug=1 BEGIN
          set nocount on;
          print @SQL;
     END
     ELSE BEGIN
          exec(@SQL);
     END    
END
GO

If I call the SP with the debug option:

SET NOCOUNT ON;

DECLARE @return_value int

EXEC    @return_value = [dbo].[sp_CreateDataChangedTrigger]
        @TableName = N'dim_Status',
        @TableKey = N'StatusID',
        @Debug = 1

SELECT  'Return Value' = @return_value

GO

Then submit the results from the Messages window, it works fine.

But, when I turn the @Debug switch off, I get these error messages:

Msg 102, Level 15, State 1, Line 10
Incorrect syntax near 'GO'.
Msg 111, Level 15, State 1, Line 13
'CREATE TRIGGER' must be the first statement in a query batch.
Msg 102, Level 15, State 1, Line 51
Incorrect syntax near 'GO'.

Thanks...

回答1:

1) EXEC[UTE] can execute only T-SQL statements.

GO is not T-SQL statement.

GO is not a Transact-SQL statement; it is a command recognized by the sqlcmd and osql utilities and SQL Server Management Studio Code editor. SQL Server utilities interpret GO as a signal that they should send the current batch of Transact-SQL statements to an instance of SQL Server.

2) You could replace

     SET @SQL = '
--Drop Trigger
BEGIN TRY
     DROP TRIGGER [dbo].[TR_' + @TableName + '_Audit]
END TRY
BEGIN CATCH
END CATCH
GO

--Create Trigger
CREATE TRIGGER [dbo].[TR_' + @TableName + '_Audit]

with

DECLARE @TriggerName SYSNAME;
SET @TriggerName = 'TR_' + @TableName + '_Audit';

IF EXISTS (
    SELECT  *
    FROM    sys.triggers 
    WHERE   parent_id = OBJECT_ID(@TableName)
    AND     name = @TriggerName
)
BEGIN
     SET @SQL = N'DROP TRIGGER [dbo].' + QUOTENAME(@TriggerName);
     EXEC(@SQL);
END

    SET @SQL = '
--Create Trigger
CREATE TRIGGER [dbo].[TR_' + @TableName + '_Audit]

or (better)

with

DECLARE @TriggerName SYSNAME;
SET @TriggerName = 'TR_' + @TableName + '_Audit';

IF NOT EXISTS (
    SELECT  *
    FROM    sys.triggers 
    WHERE   parent_id = OBJECT_ID(@TableName)
    AND     name = @TriggerName
)
BEGIN
     SET @SQL = N'CREATE TRIGGER [dbo].' + QUOTENAME(@TriggerName) + 'ON ' + @TableName + ' AFTER INSERT, UPDATE, DELETE AS BEGIN SELECT NULL END';
     EXEC(@SQL);
END

    SET @SQL = '
--Alter Trigger
ALTER TRIGGER [dbo].[TR_' + @TableName + '_Audit]

Note: The object's name should be NVARCHAR(128) or SYSNAME.



回答2:

You must execute each part of command in single exec() command.

Try this:

CREATE PROCEDURE sp_CreateDataChangedTrigger
    -- Add the parameters for the stored procedure here
    @TableName varchar(255), 
    @TableKey  varchar(255),
    @Debug     bit=1
AS
BEGIN
    -- SET NOCOUNT ON added to prevent extra result sets from
    -- interfering with SELECT statements.
    SET NOCOUNT ON;

     DECLARE @SQLPart1 varchar(max);
     DECLARE @SQLPart2 varchar(max);

     SET @SQLPart1 = '
--Drop Trigger
BEGIN TRY
     DROP TRIGGER [dbo].[TR_' + @TableName + '_Audit]
END TRY
BEGIN CATCH
END CATCH
'

SET @SQLPart2 = '
--Create Trigger
CREATE TRIGGER [dbo].[TR_' + @TableName + '_Audit]
     ON [dbo].[' + @TableName + ']
     AFTER INSERT, UPDATE, DELETE
AS
BEGIN
     SET NOCOUNT ON
     DECLARE @event_type [char]

     --Get Event Type
     IF EXISTS(SELECT * FROM INSERTED)
     IF EXISTS(SELECT * FROM DELETED)
          SELECT @event_type = ''U''
     ELSE
          SELECT @event_type = ''I''
     ELSE
     IF EXISTS(SELECT * FROM deleted)
          SELECT @event_type = ''D''
     ELSE
     --no rows affected - cannot determine event
          SELECT @event_type = ''K''

     IF @event_type IN (''I'',''U'') BEGIN
          DECLARE @CurrentUserID INT;
          SELECT  @CurrentUserID = u.UserID
          FROM    [dbo].[dim_Users] u
          WHERE   u.[Username] = dbo.udfUserName()

          UPDATE  t
          SET     DateModified = GETDATE(),
                  WhoModifiedID = @CurrentUserID
          FROM    INSERTED e
          JOIN    [dbo].[' + @TableName + '] t ON e.[' + @TableKey + '] = t.[' + @TableKey + ']
     END

     IF @event_type = ''D'' BEGIN
          no_op:  --Nothing for now
     END
END
';

     IF @Debug=1 BEGIN
          set nocount on;
          print @SQLPart1;
          print @SQLPart2;
     END
     ELSE BEGIN
          exec(@SQLPart1);
          exec(@SQLPart2);
     END    
END
GO


回答3:

The error says what the problem is exactly. You are creating a dynamic sql statement to be run all at once by the EXEC statement. GO is a batch separator for use in a command line interface or SSMS. It separates multiple statements to be executed separately. Therefore, you cannot have GO in query statements to be executed by EXEC or sp_executesql. Simply remove the GO statement, or create two queries to be run by two EXEC statements.