在BeginTry Begin TRANSACTION之前的临时存储过程会导致问题

时间:2019-01-07 16:42:45

标签: sql-server

我正在编写一个脚本,以使5个不同的列可为空(我们要确保在进行强制删除之前,系统能够完美运行)。我的存储过程可以正常工作,并且已经过测试。

但是,当我尝试整体运行此脚本时,该脚本将失败或无法使列可为空。我认为这是由于BEGIN TRYBEGIN TRANSACTION导致的。似乎正在尝试在每次运行时创建存储过程?

  1. 在脚本开始时创建临时存储过程,然后运行BEGIN TRANSACTION使这些列为空的正确方法是什么?

  2. 我应该摆脱BEGIN TRANS并仅保留BEGIN TRY吗?

我得到的错误是

  

Msg 50000,第16级,状态9,第222行

似乎有时它尝试再次创建存储过程,即使该存储过程已经存在。我应该继续将其隔离在其他脚本中吗?

-- Verify that the stored procedure does not already exist.  
IF OBJECT_ID ( '#MakeColumnsNullable', 'P' ) IS NOT NULL   
    DROP PROCEDURE #MakeColumnsNullable;  
GO 

-- create a temporary stored procedure to Drop Constraints
CREATE PROCEDURE #MakeColumnsNullable
    @tableName VARCHAR(255),
    @columnName VARCHAR(255)
AS
BEGIN
    DECLARE @sql NVARCHAR(MAX);
    DECLARE @columnType NVARCHAR(MAX);
    DECLARE @constraintname SYSNAME;
    DECLARE @objectid INT 

    BEGIN 
        --Make all columns that will later be deleted NULLABLE 
        SET @columnType = (SELECT DATA_TYPE
                           FROM INFORMATION_SCHEMA.COLUMNS
                           WHERE (TABLE_NAME = REPLACE(@tableName, 'NPI.', '') 
                                  OR TABLE_NAME = REPLACE(@tableName, 'FastTrak.', ''))
                             AND COLUMN_NAME = @columnName)

        IF EXISTS(SELECT 1 FROM sys.columns 
                  WHERE Name = @columnName
                    AND Object_ID = Object_ID(@tableName))
        BEGIN
            SET @sql = N'ALTER TABLE ' + @tableName + ' ALTER COLUMN ' + @columnName + ' ' + @columnType + ' NULL '
            EXEC (@sql)
        END
    END
END
GO

BEGIN TRY
    BEGIN TRANSACTION
        EXEC #MakeColumnsNullable 'NPI.Table1', 'CreateID';
        EXEC #MakeColumnsNullable 'NPI.Table1', 'CreateComponentID';
        EXEC #MakeColumnsNullable 'NPI.Table1', 'UpdateID';
        EXEC #MakeColumnsNullable 'NPI.Table1', 'UpdateComponentID';
        EXEC #MakeColumnsNullable 'NPI.Table1', 'DelFlag';

        EXEC #MakeColumnsNullable 'NPI.Table2', 'CreateID';
        EXEC #MakeColumnsNullable 'NPI.Table2', 'CreateComponentID';
        EXEC #MakeColumnsNullable 'NPI.Table2', 'UpdateID';
        EXEC #MakeColumnsNullable 'NPI.Table2', 'UpdateComponentID';
        EXEC #MakeColumnsNullable 'NPI.Table2', 'DelFlag';

        EXEC #MakeColumnsNullable 'NPI.Table3', 'CreateID';
        EXEC #MakeColumnsNullable 'NPI.Table3', 'CreateComponentID';
        EXEC #MakeColumnsNullable 'NPI.Table3', 'UpdateID';
        EXEC #MakeColumnsNullable 'NPI.Table3', 'UpdateComponentID';
        EXEC #MakeColumnsNullable 'NPI.Table3', 'DelFlag';

    COMMIT TRANSACTION;

    --After COMMIT TRANSACTION drop the procedure
    DROP PROCEDURE #MakeColumnsNullable;

    /* Stored procedures TO BE REMOVED */
    IF EXISTS (SELECT * 
               FROM sys.objects 
               WHERE object_id = OBJECT_ID(N'NPI.OutdatedSproc1') 
                 AND type IN (N'P', N'PC'))
    BEGIN
        -- Drop deprecated stored procedure OutdatedSproc1
        EXEC('DROP PROCEDURE NPI.OutdatedSproc1')
            PRINT ' NPI.OutdatedSproc1DROPPED'
    END

    IF EXISTS (SELECT * 
               FROM sys.objects 
               WHERE object_id = OBJECT_ID(N'NPI.OutdatedSproc2') 
                 AND type IN (N'P', N'PC'))
    BEGIN
        -- Drop Deprecated stored procedure OutdatedSproc2
        EXEC('DROP PROCEDURE NPI.OutdatedSproc2');
        PRINT ' NPI.OutdatedSproc2DROPPED';
    END

    IF EXISTS (SELECT * 
               FROM sys.objects 
               WHERE object_id = OBJECT_ID(N'NPI.OutdatedSproc3') 
                 AND type IN (N'P', N'PC'))
    BEGIN
        -- Drop deprecated stored procedure OutdatedSproc3
        EXEC('DROP PROCEDURE NPI.OutdatedSproc3');
        PRINT ' NPI.OutdatedSproc3DROPPED';
    END
END TRY
BEGIN CATCH
    DECLARE @Error INT = @@ERROR;

    ROLLBACK TRANSACTION;
    DECLARE @ErrorMessage       nvarchar(4000) = 
        'Error in ' + OBJECT_NAME(@@PROCID) + ': ' + ERROR_MESSAGE();
    DECLARE @ErrorSeverity  INT = ERROR_SEVERITY();
    DECLARE @ErrorState     INT = ERROR_STATE();

    PRINT @ErrorMessage;
    PRINT @ErrorSeverity;
    PRINT @ErrorState;
END CATCH

2 个答案:

答案 0 :(得分:0)

我的建议基本上是,在全局级别上创建存储过程(临时)并使用单独的脚本,其他问题:

  
      
  1. 在脚本开始时创建临时存储过程然后运行BEGIN TRANSACTION使这些列可为空的正确方法是什么?
  2.   

答案:与顺序无关(我认为应该在单独的脚本中创建并使用全局临时存储过程),如果您决定保留TRANSACTION是因为您正在寻找根据您的经验,更改中的原子性是唯一的,我只关心表的大小以及是否需要阻塞和消耗许多资源。

  
      
  1. 我应该摆脱BEGIN TRANS并仅保留BEGIN TRY吗?
  2.   

答案:您是否需要原子性作为此更改的一部分,如果答案是否定的,您只能保留BEGIN TRY。

答案 1 :(得分:0)

谢谢大家的反馈,我意识到有些错误的地方:

  1. 该代码块未完全位于 TRANSACTION 块中,这将导致存储过程在已存在时尝试再次创建
  2. 由于未使用适当的方法来查找 TEMP 存储过程,因此已将其更改为每次我们运行此脚本时都要检查
  3. 正如@ Steve-o169所说,错误的处理方式存在问题,我删除了 ERROR_MESSAGE ()和 RAISEERROR (),因为这两个都是问题。改用 ltrim(str(@@ ERROR)),以便我们可以正确看到错误
  4. 这帮助我看到了此存储过程失败的原因之一是因为列具有依赖于该列的索引,在进行了更多研究之后,似乎使列在SQL眼中可为空就像删除它并重新创建它。因此,存储过程将失败,因为它的索引取决于列。我添加了删除该索引的逻辑,以使该列可为空,然后重新创建索引。
  5. CATCH 块中添加了 THROW

谢谢大家的帮助,希望这对以后的人有所帮助。

正确的存储过程:

-- Verify that the stored procedure does not already exist.  
IF OBJECT_ID('tempdb..#MakeColumnsNullable') IS NOT NULL
BEGIN
    DROP PROC #MakeColumnsNullable
END
GO

-- create a temporary stored procedure to Drop Constraints
CREATE PROCEDURE #MakeColumnsNullable
@tableName varchar(255),
@columnName varchar(255)
AS
BEGIN
    DECLARE @sql NVARCHAR(MAX);
    DECLARE @columnType NVARCHAR(MAX);
    DECLARE @constraintname SYSNAME;
    DECLARE @dropIndexStatement NVARCHAR(MAX);
    DECLARE @createIndexStatement NVARCHAR(MAX);
    DECLARE @indexName NVARCHAR(MAX);
    DECLARE @objectid int
    BEGIN 
        --Get column type 
        SET @columnType = (SELECT DATA_TYPE
            FROM INFORMATION_SCHEMA.COLUMNS
                WHERE (TABLE_NAME = REPLACE(@tableName, 'NPI.', '') OR TABLE_NAME = REPLACE(@tableName, 'FastTrak.', ''))
                    AND COLUMN_NAME = @columnName)

        -- need to see if there are any references to indexes
        IF EXISTS(SELECT name FROM sys.indexes WHERE object_id = object_id(@tableName) AND filter_definition like '%'+ @columnName +'%' AND type_desc = 'NONCLUSTERED')
        BEGIN 
            set @indexName = (SELECT name FROM sys.indexes WHERE object_id = object_id(@tableName) AND filter_definition like '%'+ @columnName +'%' AND type_desc = 'NONCLUSTERED');
            set @dropIndexStatement = N'DROP INDEX ' + @indexName + ' ON ' + @tableName + ' '
            set @createIndexStatement = N'CREATE INDEX ' + @indexName + ' ON ' + @tableName + ' (' + @columnName + ') '

            --PRINT @dropIndexStatement;
            EXEC (@dropIndexStatement)
        END

        -- Make the column nullable
        IF EXISTS(SELECT 1 FROM sys.columns 
          WHERE Name = @columnName
          AND Object_ID = Object_ID(@tableName))
        BEGIN
            set @sql = N'alter table ' + @tableName + ' ALTER COLUMN ' + @columnName + ' ' + @columnType + ' NULL '
            --PRINT @sql;
            EXEC (@sql)
        END

        --RECREATE INDEX IF @createIndexStatement has been SET
        IF (@createIndexStatement IS NOT NULL) OR (LEN(@createIndexStatement) > 0)
        BEGIN
            --PRINT @createIndexStatement;
            EXEC (@createIndexStatement)
        END
    END
END
GO

BEGIN TRY
    BEGIN TRANSACTION
        EXEC #MakeColumnsNullable 'NPI.Table1', 'CreateID';
        EXEC #MakeColumnsNullable 'NPI.Table1', 'CreateComponentID';
        EXEC #MakeColumnsNullable 'NPI.Table1', 'UpdateID';
        EXEC #MakeColumnsNullable 'NPI.Table1', 'UpdateComponentID';
        EXEC #MakeColumnsNullable 'NPI.Table1', 'DelFlag';

        EXEC #MakeColumnsNullable 'NPI.Table2', 'CreateID';
        EXEC #MakeColumnsNullable 'NPI.Table2', 'CreateComponentID';
        EXEC #MakeColumnsNullable 'NPI.Table2', 'UpdateID';
        EXEC #MakeColumnsNullable 'NPI.Table2', 'UpdateComponentID';
        EXEC #MakeColumnsNullable 'NPI.Table2', 'DelFlag';

        EXEC #MakeColumnsNullable 'NPI.Table3', 'CreateID';
        EXEC #MakeColumnsNullable 'NPI.Table3', 'CreateComponentID';
        EXEC #MakeColumnsNullable 'NPI.Table3', 'UpdateID';
        EXEC #MakeColumnsNullable 'NPI.Table3', 'UpdateComponentID';
        EXEC #MakeColumnsNullable 'NPI.Table3', 'DelFlag';

        /* SPROCS TO BE REMOVED */
        IF OBJECT_ID('tempdb..#MakeColumnsNullable') IS NOT NULL
        BEGIN
            DROP PROC #MakeColumnsNullable
        END

        IF EXISTS (SELECT * 
               FROM sys.objects 
               WHERE object_id = OBJECT_ID(N'NPI.OutdatedSproc1') 
                 AND type IN (N'P', N'PC'))
        BEGIN
            -- Drop deprecated stored procedure OutdatedSproc1
            EXEC('DROP PROCEDURE NPI.OutdatedSproc1')
                PRINT ' NPI.OutdatedSproc1DROPPED'
        END

        IF EXISTS (SELECT * 
                FROM sys.objects 
                WHERE object_id = OBJECT_ID(N'NPI.OutdatedSproc2') 
                    AND type IN (N'P', N'PC'))
        BEGIN
            -- Drop Deprecated stored procedure OutdatedSproc2
            EXEC('DROP PROCEDURE NPI.OutdatedSproc2');
            PRINT ' NPI.OutdatedSproc2DROPPED';
        END

        IF EXISTS (SELECT * 
                FROM sys.objects 
                WHERE object_id = OBJECT_ID(N'NPI.OutdatedSproc3') 
                    AND type IN (N'P', N'PC'))
        BEGIN
            -- Drop deprecated stored procedure OutdatedSproc3
            EXEC('DROP PROCEDURE NPI.OutdatedSproc3');
            PRINT ' NPI.OutdatedSproc3DROPPED';
        END
    COMMIT TRANSACTION;    
END TRY
BEGIN CATCH
    IF @@TRANCOUNT > 0
    BEGIN
        PRINT 'TRAN ROLLEDBACK FOR SOME REASON'; 
        ROLLBACK TRANSACTION 
    END

    DECLARE @ErrorMessage nvarchar(4000) = 'Error is ' + ltrim(str(@@ERROR)) + '.';
    DECLARE @ErrorSeverity  INT = ERROR_SEVERITY();
    DECLARE @ErrorState     INT = ERROR_STATE();

    PRINT @ErrorMessage;
    PRINT @ErrorSeverity;
    PRINT @ErrorState;

    THROW;
END CATCH