簡體   English   中英

在現有約束/規則/程序上更改 Quoted_Identifier

[英]Changing Quoted_Identifier on existing Constraints / Rules / Procedures

我目前正在使用 2008-r2 服務器上的舊式數據庫,該數據庫使用了許多在 Quoted Identifier 設置為 off 的情況下創建的對象。

我主要看這些類型:

CHECK_CONSTRAINT
DEFAULT_CONSTRAINT
RULE
SQL_SCALAR_FUNCTION
SQL_STORED_PROCEDURE
SQL_TRIGGER
VIEW

我現在正在嘗試更改引用標識符設置,因為我發現我什至無法更改約束,這讓我立即感到困惑。

對於約束:我想我必須以某種方式制作臨時克隆/副本,刪除原始副本,然后使用副本和 Quoted_Identifier 設置為 ON 重新創建它們,但我真的不知道如何執行此操作或如何執行此操作自動執行此操作,因為我的 SQL 技能有限。 有人可以幫助我嗎? 或者有人知道更簡單的替代方法嗎?

我在安裝過程中遇到了一個錯誤,導致 QUOTED_IDENTIFIER 在大量對象上隨機打開/關閉(問題涉及過程、函數、觸發器和視圖......)。

由於使用過濾索引和查詢通知之類的東西需要 QUOTED_IDENTIFIER ON,我想要一種方法來找到它關閉的所有內容並將其打開。

在研究這個站點上的問題時,我發現了這個(以及其他一些)帖子,但我發現沒有一個可以在不重新生成所有 SQL 腳本的情況下做到這一點的好方法(我確實有數千個需要修復的對象)或編寫 C# 代碼。

所以我開發了一種基於 SQL 的處理方式。 這將重新編譯所有 proc,並生成一個列表,列出由於某種原因無法編譯的任何程序。 我知道這不能處理不同的模式(dbo vs. sales vs.whatever),但您可以根據需要對其進行調整。 在我的情況下,我不需要擔心。

    SET NOCOUNT ON

    -- MAKE SURE THIS IS ON!!
    SET QUOTED_IDENTIFIER ON

    --- Used in try/catch below
    DECLARE @ErrorMessage nvarchar(4000);
    DECLARE @ErrorSeverity int;
    DECLARE @ErrorState int;

    DECLARE @name sysname
    DECLARE @type char(2)
    DECLARE @objType nvarchar(50)
    DECLARE @createCommand nvarchar(max)
    DECLARE @dropCommand nvarchar(max)
    DECLARE @success bit

    IF OBJECT_ID(N'tempdb..#ProcList', N'U') IS NOT NULL DROP TABLE #ProcList

    CREATE TABLE #ProcList
    (
        name            sysname         NOT NULL PRIMARY KEY,
        id              int             NOT NULL,
        type            char(2)         NOT NULL,
        definition      nvarchar(max)   NULL,
        alterstmt       nvarchar(max)   NULL,
        processed       bit             NOT NULL,
        successful      bit             NOT NULL
    )

    --- Build the list of objects that have quoted_identifier off
    INSERT INTO #ProcList
    SELECT 
        so.name, 
        so.object_id, 
        so.type, 
        sm.definition, 
        NULL, 
        0, 
        0
    FROM sys.objects so
        INNER JOIN sys.sql_modules sm
            ON so.object_id = sm.object_id
    WHERE 
        LEFT(so.name, 3) NOT IN ('sp_', 'xp_', 'ms_')
        AND sm.uses_quoted_identifier = 0
    ORDER BY 
        name

    -- Get the first object
    SELECT @name = MIN(name) FROM #ProcList WHERE processed = 0

    --- As long as we have one, keep going
    WHILE (@name IS NOT NULL)
    BEGIN

        SELECT
            @createCommand = definition,
            @type = type
        FROM #ProcList 
        WHERE name = @name

        --- Determine what type of object it is
        SET @objType = CASE @type
                WHEN 'P'  THEN 'PROCEDURE' 
                WHEN 'TF' THEN 'FUNCTION'
                WHEN 'IF' THEN 'FUNCTION'
                WHEN 'FN' THEN 'FUNCTION'
                WHEN 'V'  THEN 'VIEW'
                WHEN 'TR' THEN 'TRIGGER'
            END

        --- Create the drop command
        SET @dropCommand = 'DROP ' + @objType + ' ' + @name

        --- record the drop statement that we are going to execute
        UPDATE #ProcList 
        SET 
            processed = 1, 
            alterstmt = @dropCommand 
        WHERE name = @name

        --- Assume we will not succeed
        SET @success = 0

        BEGIN TRANSACTION

        --- Drop the current proc
        EXEC sp_executesql @dropCommand

        BEGIN TRY

            --- Execute the create statement from the definition
            EXEC sp_executesql @createCommand

            --- If we reached this point, it all worked
            SET @success = 1

            COMMIT

        END TRY
        BEGIN CATCH

            --- oops something went wrong
            SELECT
                @ErrorMessage = ERROR_MESSAGE(),
                @ErrorSeverity = ERROR_SEVERITY(),
                @ErrorState = ERROR_STATE();

            PRINT 'Error processing ' + @name
            RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @name)

            --- Undo the transaction, which undoes the drop above
            ROLLBACK

        END CATCH

        --- At this point, there should be no open transactions
        IF @@TRANCOUNT > 0 
        BEGIN
            PRINT 'ERROR... transaction count not right!!'
            ROLLBACK
            RETURN
        END

        --- check to make sure the object still exists after executing the alter statement, and that we didn't detect an earlier error
        --- If it's all good, then mark the proc as having been successful
        IF (
            @success = 1
            AND EXISTS (
                SELECT name 
                FROM sys.objects so 
                    INNER JOIN sys.sql_modules sm 
                        ON so.object_id = sm.object_id 
                WHERE name = @name
            )
        )
        BEGIN
            UPDATE #ProcList SET successful = 1 WHERE name = @name
        END      

        -- Get the next one... if none are left the result will be NULL
        SELECT @name = MIN(name) FROM #ProcList where processed = 0

    END

    -- What wasn't successful??
    SELECT * 
    FROM #ProcList 
    WHERE successful = 0 
    ORDER BY name 

編寫此數據庫的腳本,刪除所有 Quoted_Identifier OFF,從腳本重新創建數據庫並重新導入數據(該向導包含在 MS SS 中)。

這種解決方案可能有很多風味。 使用動態 sql /smo 技術解決的類似問題: 更改數據庫中所有存儲過程的 ANSI_NULLS 設置

我已經更改了@Earl 編寫的腳本(見上文),以便它執行 CREATE OR ALTER,並且也適用於 SQL Server 2016 及更高版本的模式。

它仍然不完美,因為找到“CREATE”部分並不簡單。

set nocount ON

    -- MAKE SURE THIS IS ON!!
    SET QUOTED_IDENTIFIER ON

    DROP TABLE if exists #ProcList

    CREATE TABLE #ProcList
    (
        name            sysname         NOT NULL /*PRIMARY KEY*/,
        SchemaName      sysname         not null,
        id              int             NOT NULL,
        type            char(2)         NOT NULL,
        definition      nvarchar(max)   NULL,
        alterstmt       nvarchar(max)   NULL,
        processed       bit             NOT NULL,
        successful      bit             NOT null,
        primary key clustered (SchemaName, name)
    )


    --- Build the list of objects that have quoted_identifier off
    INSERT INTO #ProcList
    SELECT 
        so.name, 
        schema_name(so.schema_id),
        so.object_id, 
        so.type, 
        sm.definition, 
        NULL, 
        0, 
        0
    FROM sys.objects so
        INNER JOIN sys.sql_modules sm
            ON so.object_id = sm.object_id
    WHERE 
        LEFT(so.name, 3) NOT IN ('sp_', 'xp_', 'ms_')
        AND sm.uses_quoted_identifier = 0
    ORDER BY 
        so.name

    --- Used in try/catch below
    DECLARE @ErrorMessage nvarchar(4000);
    DECLARE @ErrorSeverity int;
    DECLARE @ErrorState int;

    DECLARE @name sysname, @SchemaName sysname 
    declare @type char(2)
    DECLARE @objType nvarchar(50)
    DECLARE @createCommand nvarchar(max)
    DECLARE @dropCommand nvarchar(max)
    DECLARE @success bit

    -- Get the first object
    SELECT top (1) @name = name, @SchemaName= SchemaName FROM #ProcList WHERE processed = 0 order by SchemaName, name

    --- As long as we have one, keep going
    WHILE (@name IS NOT NULL)
    BEGIN
        raiserror ('at %s %s', 10, 1, @SchemaName, @name) with nowait
        SELECT
            @createCommand = definition,
            @type = type
        FROM #ProcList 
        WHERE name = @name

        --- Determine what type of object it is
        SET @objType = CASE @type
                WHEN 'P'  THEN 'PROCEDURE' 
                WHEN 'TF' THEN 'FUNCTION'
                WHEN 'IF' THEN 'FUNCTION'
                WHEN 'FN' THEN 'FUNCTION'
                WHEN 'V'  THEN 'VIEW'
                WHEN 'TR' THEN 'TRIGGER'
            END

        --- Create the drop command
        SET @dropCommand = 'DROP ' + @objType + ' ' + quotename(@SchemaName) + '.' + quotename(@name)

        --- record the drop statement that we are going to execute
        UPDATE #ProcList 
        SET 
            processed = 1, 
            alterstmt = @dropCommand 
        WHERE name = @name and SchemaName = @SchemaName

        --- Assume we will not succeed
        SET @success = 0

        BEGIN TRANSACTION

        --- Drop the current proc
        --EXEC sp_executesql @dropCommand

        BEGIN TRY

            set @createCommand = replace(@createCommand, 'Create proc', 'Create or Alter Proc')
            set @createCommand = replace(@createCommand, 'Create  proc', 'Create or Alter Proc')
            set @createCommand = replace(@createCommand, 'Create   proc', 'Create or Alter Proc')
            set @createCommand = replace(@createCommand, 'Create    proc', 'Create or Alter Proc')
            set @createCommand = replace(@createCommand, 'Create     proc', 'Create or Alter Proc')
            set @createCommand = replace(@createCommand, 'Create' + char(13) + char(10) + 'proc', 'Create or Alter Proc')
            set @createCommand = replace(@createCommand, 'Create' + char(10) + 'proc', 'Create or Alter Proc')
            set @createCommand = replace(@createCommand, 'Create view', 'Create or Alter view')
            set @createCommand = replace(@createCommand, 'Create  view', 'Create or Alter view')
            set @createCommand = replace(@createCommand, 'Create   view', 'Create or Alter view')
            set @createCommand = replace(@createCommand, 'Create    view', 'Create or Alter view')
            --- Execute the create statement from the definition
            EXEC sp_executesql @createCommand

            --- If we reached this point, it all worked
            SET @success = 1

            COMMIT

        END TRY
        BEGIN CATCH

            --- oops something went wrong
            SELECT
                @ErrorMessage = ERROR_MESSAGE(),
                @ErrorSeverity = ERROR_SEVERITY(),
                @ErrorState = ERROR_STATE();

            PRINT 'Error processing ' + @name
            exec dbo.LongPrint  @createCommand
            
            RAISERROR (@ErrorMessage, @ErrorSeverity, @ErrorState, @name)

            --- Undo the transaction, which undoes the drop above
            ROLLBACK

        END CATCH

        --- At this point, there should be no open transactions
        IF @@TRANCOUNT > 0 
        BEGIN
            PRINT 'ERROR... transaction count not right!!'
            ROLLBACK
            RETURN
        END

        --- check to make sure the object still exists after executing the alter statement, and that we didn't detect an earlier error
        --- If it's all good, then mark the proc as having been successful
        IF (
            @success = 1
            AND EXISTS (
                SELECT so.name 
                FROM sys.objects so 
                    INNER JOIN sys.sql_modules sm 
                        ON so.object_id = sm.object_id 
                WHERE so.name = @name and schema_name(so.schema_id) = @SchemaName
            )
        )
        BEGIN
            UPDATE #ProcList SET successful = 1 WHERE name = @name and SchemaName = @SchemaName
        END      

        SELECT @name = null
        -- Get the next one... if none are left the @name will be NULL
        SELECT top (1) @name = name, @SchemaName= SchemaName FROM #ProcList WHERE processed = 0 order by SchemaName, name
    END

    -- What wasn't successful??
    SELECT * 
    FROM #ProcList 
    WHERE successful = 0 
    ORDER BY SchemaName, name 

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM