2

我目前正在使用 2008-r2 服务器上的旧式数据库,该数据库使用了许多在 Quoted Identifier 设置为 off 的情况下创建的对象。

我主要看这些类型:

CHECK_CONSTRAINT
DEFAULT_CONSTRAINT
RULE
SQL_SCALAR_FUNCTION
SQL_STORED_PROCEDURE
SQL_TRIGGER
VIEW

我现在正在尝试更改引用标识符设置,因为我发现我什至无法更改约束,这让我立即感到困惑。

对于约束:我想我必须以某种方式制作一个临时克隆/副本,删除原始文件,然后使用副本重新创建它们,并将 Quoted_Identifier 设置为 ON,但我真的不知道如何执行此操作或如何执行此操作自动执行此操作,因为我的 SQL 技能有限。有人可以帮助我吗?或者有人知道更简单的替代方法吗?

4

3 回答 3

5

我在安装过程中遇到了一个错误,导致 QUOTED_IDENTIFIER 在大量对象上随机打开/关闭(问题涉及过程、函数、触发器和视图......)。

因为使用过滤索引和查询通知之类的东西需要 QUOTED_IDENTIFIER ON,所以我想要一种方法来找到所有关闭的索引并将其打开。

在研究这个网站上的问题时,我发现了这个(以及其他一些)帖子,但我发现没有一个很好的方法可以在不重新生成所有 SQL 脚本的情况下做到这一点(我实际上有数千个需要修复的对象)或编写 C# 代码。

所以我开发了一种基于 SQL 的方法来处理它。这会重新编译所有 proc,并生成一个由于某种原因无法编译的列表。我知道这不能处理不同的模式(dbo 与销售与其他),但您可以根据需要对其进行调整。在我的情况下,我不需要担心这一点。

    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 
于 2014-06-25T14:02:49.177 回答
0

编写此数据库的脚本,删除所有 Quoted_Identifier OFF,从脚本重新创建数据库并重新导入数据(该向导包含在 MS SS 中)。

这个解决方案可能有很多味道。使用动态 sql /smo 技术解决了类似的问题: Change the ANSI_NULLS setting for all Stored Procedures in the Database

于 2014-01-24T10:52:50.253 回答
0

我已经更改了 @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 
于 2021-03-18T11:55:45.513 回答