【问题标题】:Change collations of all columns of all tables in SQL Server更改 SQL Server 中所有表的所有列的排序规则
【发布时间】:2013-08-09 23:22:30
【问题描述】:

我导入了一个数据库,其中包含一些数据以与另一个数据库进行比较。

目标数据库有排序规则Latin1_General_CI_AS,源数据库有SQL_Latin1_General_CP1_CI_AS

我确实使用 SQL Server Management Studio 将源数据库的排序规则更改为 Latin1_General_CI_AS。但是里面的表和列仍然使用旧的排序规则。

我知道我可以使用以下方法更改列:

ALTER TABLE [table] 
ALTER COLUMN [column] VARCHAR(100) COLLATE Latin1_General_CI_AS

但我必须对里面的所有表和所有列都这样做。

在我知道之前开始编写一个存储过程,它读取所有表和varchar 类型的所有列,并在表和列游标循环中更改它们...

有没有人知道一种更简单的方法,或者是唯一的方法,用一个脚本在一个过程中的所有表中运行?

【问题讨论】:

标签: sql-server collate


【解决方案1】:

由于我没有找到合适的方法,所以我编写了一个脚本来执行此操作,我在这里与需要它的人共享它。该脚本运行所有用户表并收集列。如果列类型是任何 char 类型,则它会尝试将其转换为给定的排序规则。

列必须是无索引和无约束的。

如果有人对此仍有更好的解决方案,请发布!

DECLARE @collate nvarchar(100);
DECLARE @table nvarchar(255);
DECLARE @column_name nvarchar(255);
DECLARE @column_id int;
DECLARE @data_type nvarchar(255);
DECLARE @max_length int;
DECLARE @row_id int;
DECLARE @sql nvarchar(max);
DECLARE @sql_column nvarchar(max);

SET @collate = 'Latin1_General_CI_AS';

DECLARE local_table_cursor CURSOR FOR

SELECT [name]
FROM sysobjects
WHERE OBJECTPROPERTY(id, N'IsUserTable') = 1

OPEN local_table_cursor
FETCH NEXT FROM local_table_cursor
INTO @table

WHILE @@FETCH_STATUS = 0
BEGIN

    DECLARE local_change_cursor CURSOR FOR

    SELECT ROW_NUMBER() OVER (ORDER BY c.column_id) AS row_id
        , c.name column_name
        , t.Name data_type
        , c.max_length
        , c.column_id
    FROM sys.columns c
    JOIN sys.types t ON c.system_type_id = t.system_type_id
    LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
    LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
    WHERE c.object_id = OBJECT_ID(@table)
    ORDER BY c.column_id

    OPEN local_change_cursor
    FETCH NEXT FROM local_change_cursor
    INTO @row_id, @column_name, @data_type, @max_length, @column_id

    WHILE @@FETCH_STATUS = 0
    BEGIN

        IF (@max_length = -1) OR (@max_length > 4000) SET @max_length = 4000;

        IF (@data_type LIKE '%char%')
        BEGIN TRY
            SET @sql = 'ALTER TABLE ' + @table + ' ALTER COLUMN ' + @column_name + ' ' + @data_type + '(' + CAST(@max_length AS nvarchar(100)) + ') COLLATE ' + @collate
            PRINT @sql
            EXEC sp_executesql @sql
        END TRY
        BEGIN CATCH
          PRINT 'ERROR: Some index or constraint rely on the column' + @column_name + '. No conversion possible.'
          PRINT @sql
        END CATCH

        FETCH NEXT FROM local_change_cursor
        INTO @row_id, @column_name, @data_type, @max_length, @column_id

    END

    CLOSE local_change_cursor
    DEALLOCATE local_change_cursor

    FETCH NEXT FROM local_table_cursor
    INTO @table

END

CLOSE local_table_cursor
DEALLOCATE local_table_cursor

GO

【讨论】:

  • 如果您使用模式,您应该将 Select [name] from sysobjects 更改为:SELECT sys.schemas.name + '.' + sys.objects.name AS Name FROM sys.objects INNER JOIN sys.schemas ON sys.objects.schema_id = sys.schemas.schema_id WHERE type_desc = 'USER_TABLE'
  • 此代码对我有效,只有索引列必须重新创建。如果您使用此行更新您的代码,只需添加以下行将是完美的。 SET @sql = 'ALTER TABLE [' + @table + '] ALTER COLUMN [' + @column_name + '] ' + @data_type + '(' + CAST(@max_length AS nvarchar(100)) + ') COLLATE ' + @collate
  • 似乎没有改变关键字段!
【解决方案2】:

所以我再次对答案不满意。我的任务是将 JIRA 6.4.x 升级到 JIRA Software 7.x,我解决了数据库和列排序规则的特定问题。

在 SQL Server 中,如果您不删除诸如主键或外键甚至索引之类的约束,则上面提供的作为答案的脚本根本不起作用。然而,它会改变那些没有这些属性的人。这确实有问题,因为我不想手动删除所有约束并重新创建它们。该操作可能以错误告终。另一方面,创建自动化更改的脚本可能需要很长时间才能完成。

所以我找到了一种方法,只需使用 SQL Management Studio 即可进行迁移。流程如下:

  • 用别的东西重命名数据库。例如,我的是“Jira”,所以我将其重命名为“JiraTemp”。
  • 创建一个名为“Jira”的新数据库,并确保设置正确的排序规则。只需选择“选项”页面并更改排序规则。
  • 创建后,返回“JiraTemp”,右键单击它,“任务 -> 生成脚本...”。
    • 选择“编写整个数据库和所有数据库对象的脚本”。
    • 选择“保存到新的查询窗口”,然后选择“高级”
    • 将“服务器版本脚本”的值更改为所需值
    • 启用“脚本对象级权限”、“脚本所有者”和“脚本全文索引”
    • 保留其他所有内容,或根据需要进行个性化设置。
  • 一旦生成,删除“CREATE DATABASE”部分。将“JiraTemp”替换为“Jira”。
  • 运行脚本。数据库的整个数据库结构和权限现在已复制到“Jira”。
  • 在复制数据之前,我们需要禁用所有约束。在数据库“Jira”中执行以下命令:EXEC sp_msforeachtable "ALTER TABLE ? NOCHECK CONSTRAINT all"
  • 现在需要传输数据。为此,只需右键单击“JiraTemp”,然后选择“任务 -> 导出数据...”
    • 选择 SQL Server 的 OLE DB 提供程序作为数据源和目标。
    • 源数据库是“JiraTemp”
    • 目标数据库是“Jira”
    • 从技术上讲,源和目标的服务器名称相同(除非您在另一台服务器上创建了数据库)。
    • 选择“从一个或另一个表或视图复制数据”
    • 选择除视图之外的所有表。然后,当仍然突出显示时,单击“编辑映射”。勾选“启用身份插入”
    • 依次单击“确定”、“下一步”和“完成”
  • 数据传输可能需要一段时间。完成后,执行以下命令重新启用所有约束:exec sp_msforeachtable @command1="print '?'", @command2="ALTER TABLE ? WITH CHECK CHECK CONSTRAINT all"

完成后,我重新启动了 JIRA,并且我的数据库排序规则正常。希望对很多人有帮助!

【讨论】:

  • 严格执行此过程,在复制数据步骤中出现错误:“无法处理 XXXX 列,因为为其指定了多个代码页(X 和 Y)。幸运的是,它只是在一个表上,所以我从复制步骤中排除了该表,后来手动复制了它。所以这不是一个万无一失的解决方案。
  • 由于我不知道有关这已完成的环境的任何详细信息,也不知道使用了哪个版本的 SQL Server,我无法真正说出确切的原因。在谷歌上快速搜索似乎说它确实与数据传输有关。这是我为其他有问题的人找到的:dba.stackexchange.com/questions/22010/…social.msdn.microsoft.com/Forums/sqlserver/en-US/…
  • 对于那些在这里将 JIRA 升级到 7.x 系列的人来说,我发现进行 XML 备份、删除数据库、使用正确的排序规则重新创建、然后从 XML 导入更容易。见confluence.atlassian.com/adminjiraserver072/…
  • 我遇到了与 pkExec 相同的问题(代码页差异)。作为一个 worarround,我使用了以下脚本:EXEC sp_msforeachtable "PRINT 'INSERT INTO <new_db_name>.? SELECT * FROM ?;'",然后执行了插入。我希望这可以帮助某人节省一些时间...
  • 出于好奇,您使用的是什么版本的 SQL Server?
【解决方案3】:

修复nvarchar长度问题并添加NULL/NOT NULL

DECLARE @collate nvarchar(100);
DECLARE @table nvarchar(255);
DECLARE @column_name nvarchar(255);
DECLARE @column_id int;
DECLARE @data_type nvarchar(255);
DECLARE @max_length int;
DECLARE @row_id int;
DECLARE @sql nvarchar(max);
DECLARE @sql_column nvarchar(max);
DECLARE @is_Nullable bit;
DECLARE @null nvarchar(25);

SET @collate = 'Latin1_General_CI_AS';

DECLARE local_table_cursor CURSOR FOR

SELECT [name]
FROM sysobjects
WHERE OBJECTPROPERTY(id, N'IsUserTable') = 1

OPEN local_table_cursor
FETCH NEXT FROM local_table_cursor
INTO @table

WHILE @@FETCH_STATUS = 0
BEGIN

    DECLARE local_change_cursor CURSOR FOR

    SELECT ROW_NUMBER() OVER (ORDER BY c.column_id) AS row_id
        , c.name column_name
        , t.Name data_type
        , c.max_length
        , c.column_id
        , c.is_nullable
    FROM sys.columns c
    JOIN sys.types t ON c.system_type_id = t.system_type_id
    LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
    LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
    WHERE c.object_id = OBJECT_ID(@table)
    ORDER BY c.column_id

    OPEN local_change_cursor
    FETCH NEXT FROM local_change_cursor
    INTO @row_id, @column_name, @data_type, @max_length, @column_id, @is_nullable

    WHILE @@FETCH_STATUS = 0
    BEGIN

        IF (@max_length = -1) SET @max_length = 4000;
        set @null=' NOT NULL'
        if (@is_nullable = 1) Set @null=' NULL'
        if (@Data_type='nvarchar') set @max_length=cast(@max_length/2 as bigint)
        IF (@data_type LIKE '%char%')
        BEGIN TRY
            SET @sql = 'ALTER TABLE ' + @table + ' ALTER COLUMN [' + rtrim(@column_name) + '] ' + @data_type + '(' + CAST(@max_length AS nvarchar(100)) +  ') COLLATE ' + @collate + @null
            PRINT @sql
            EXEC sp_executesql @sql
        END TRY
        BEGIN CATCH
          PRINT 'ERROR: Some index or contraint rely on the column ' + @column_name + '. No conversion possible.'
          PRINT @sql
        END CATCH

        FETCH NEXT FROM local_change_cursor
        INTO @row_id, @column_name, @data_type, @max_length, @column_id, @is_Nullable

    END

    CLOSE local_change_cursor
    DEALLOCATE local_change_cursor

    FETCH NEXT FROM local_table_cursor
    INTO @table

END

CLOSE local_table_cursor
DEALLOCATE local_table_cursor

GO

【讨论】:

  • join with index_columns 并且未使用索引,实际上会导致列重复;也可以使用过滤来查找仅需要转换的列 (c.collat​​ion_name @collat​​e)
【解决方案4】:

为此,我有一个适合我的简单解决方案。

  1. 使用新排序规则创建一个新数据库。
  2. 以脚本方式导出原始数据库的数据。
  3. 使用脚本将内容导入新数据库(将 USE 语句重命名为新数据库)。

但是,如果您的数据库有触发器、过程或类似的东西 - 不仅仅是数据和表格,您需要谨慎行事。

【讨论】:

    【解决方案5】:

    我对剧本做了一点改动。

    DECLARE @collate nvarchar(100);
    DECLARE @table sysname;
    DECLARE @schema sysname;
    DECLARE @objectId int;
    DECLARE @column_name nvarchar(255);
    DECLARE @column_id int;
    DECLARE @data_type nvarchar(255);
    DECLARE @max_length int;
    DECLARE @row_id int;
    DECLARE @sql nvarchar(max);
    DECLARE @sql_column nvarchar(max);
    DECLARE @is_Nullable bit;
    DECLARE @null nvarchar(25);
    
    SET @collate = 'Latin1_General_CI_AS';
    
    DECLARE local_table_cursor CURSOR FOR
    
    SELECT tbl.TABLE_SCHEMA,[name],obj.id
    FROM sysobjects as obj
    inner join INFORMATION_SCHEMA.TABLES as tbl
    on obj.name = tbl.TABLE_NAME
    WHERE OBJECTPROPERTY(obj.id, N'IsUserTable') = 1
    
    OPEN local_table_cursor
    FETCH NEXT FROM local_table_cursor
    INTO @schema, @table, @objectId;
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
        DECLARE local_change_cursor CURSOR FOR
        SELECT ROW_NUMBER() OVER (ORDER BY c.column_id) AS row_id
            , c.name column_name
            , t.Name data_type
            , c.max_length
            , c.column_id
            , c.is_nullable
        FROM sys.columns c
        JOIN sys.types t ON c.system_type_id = t.system_type_id
        LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
        LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
        WHERE c.object_id = @objectId
        ORDER BY c.column_id
    
        OPEN local_change_cursor
        FETCH NEXT FROM local_change_cursor
        INTO @row_id, @column_name, @data_type, @max_length, @column_id, @is_nullable
    
        WHILE @@FETCH_STATUS = 0
        BEGIN
            IF (@max_length = -1) SET @max_length = 4000;
            set @null=' NOT NULL'
            if (@is_nullable = 1) Set @null=' NULL'
            if (@Data_type='nvarchar') set @max_length=cast(@max_length/2 as bigint)
            IF (@data_type LIKE '%char%')
            BEGIN TRY
                SET @sql = 'ALTER TABLE ' + @schema + '.' + @table + ' ALTER COLUMN [' + rtrim(@column_name) + '] ' + @data_type + '(' + CAST(@max_length AS nvarchar(100)) +  ') COLLATE ' + @collate + @null
                PRINT @sql
                EXEC sp_executesql @sql
            END TRY
            BEGIN CATCH
              PRINT 'ERROR: Some index or contraint rely on the column ' + @column_name + '. No conversion possible.'
              PRINT @sql
            END CATCH
    
            FETCH NEXT FROM local_change_cursor
            INTO @row_id, @column_name, @data_type, @max_length, @column_id, @is_Nullable
    
        END
    
        CLOSE local_change_cursor
        DEALLOCATE local_change_cursor
    
        FETCH NEXT FROM local_table_cursor
        INTO @schema,@table,@objectId
    
    END
    
    CLOSE local_table_cursor
    DEALLOCATE local_table_cursor
    
    GO
    

    【讨论】:

    • 你能告诉我,你做了哪些改变吗?无论如何,我已经成功地使用了你的脚本。非常感谢。
    【解决方案6】:

    修复长度问题nvarchar(包括最大值),包括文本并添加NULL/NOT NULL。

    USE [put your database name here];
    
    begin tran
    
    DECLARE @collate nvarchar(100);
    DECLARE @table nvarchar(255);
    DECLARE @column_name nvarchar(255);
    DECLARE @column_id int;
    DECLARE @data_type nvarchar(255);
    DECLARE @max_length int;
    DECLARE @max_length_str nvarchar(100);
    DECLARE @is_nullable bit;
    DECLARE @row_id int;
    DECLARE @sql nvarchar(max);
    DECLARE @sql_column nvarchar(max);
    
    SET @collate = 'Latin1_General_CI_AS';
    
    DECLARE local_table_cursor CURSOR FOR
    
    SELECT [name]
    FROM sysobjects
    WHERE OBJECTPROPERTY(id, N'IsUserTable') = 1
    ORDER BY [name]
    
    OPEN local_table_cursor
    FETCH NEXT FROM local_table_cursor
    INTO @table
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
    
        DECLARE local_change_cursor CURSOR FOR
    
        SELECT ROW_NUMBER() OVER (ORDER BY c.column_id) AS row_id
            , c.name column_name
            , t.Name data_type
            , col.CHARACTER_MAXIMUM_LENGTH
            , c.column_id
            , c.is_nullable
        FROM sys.columns c
        JOIN sys.types t ON c.system_type_id = t.system_type_id
        JOIN INFORMATION_SCHEMA.COLUMNS col on col.COLUMN_NAME = c.name and c.object_id = OBJECT_ID(col.TABLE_NAME)
        LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
        LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
        WHERE c.object_id = OBJECT_ID(@table) AND (t.Name LIKE '%char%' OR t.Name LIKE '%text%') 
        AND c.collation_name <> @collate
        ORDER BY c.column_id
    
        OPEN local_change_cursor
        FETCH NEXT FROM local_change_cursor
        INTO @row_id, @column_name, @data_type, @max_length, @column_id, @is_nullable
    
        WHILE @@FETCH_STATUS = 0
        BEGIN
    
            set @max_length_str = @max_length
            IF (@max_length = -1) SET @max_length_str = 'max'
            IF (@max_length > 4000) SET @max_length_str = '4000'
    
            BEGIN TRY
                SET @sql =
                CASE 
                    WHEN @data_type like '%text%' 
                    THEN 'ALTER TABLE ' + @table + ' ALTER COLUMN [' + @column_name + '] ' + @data_type + ' COLLATE ' + @collate + ' ' + CASE WHEN @is_nullable = 0 THEN 'NOT NULL' ELSE 'NULL' END
                    ELSE 'ALTER TABLE ' + @table + ' ALTER COLUMN [' + @column_name + '] ' + @data_type + '(' + @max_length_str + ') COLLATE ' + @collate + ' ' + CASE WHEN @is_nullable = 0 THEN 'NOT NULL' ELSE 'NULL' END
                END
                --PRINT @sql
                EXEC sp_executesql @sql
            END TRY
            BEGIN CATCH
              PRINT 'ERROR (' + @table + '): Some index or constraint rely on the column ' + @column_name + '. No conversion possible.'
              --PRINT @sql
            END CATCH
    
            FETCH NEXT FROM local_change_cursor
            INTO @row_id, @column_name, @data_type, @max_length, @column_id, @is_nullable
    
        END
    
        CLOSE local_change_cursor
        DEALLOCATE local_change_cursor
    
        FETCH NEXT FROM local_table_cursor
        INTO @table
    
    END
    
    CLOSE local_table_cursor
    DEALLOCATE local_table_cursor
    
    commit tran
    
    GO
    

    注意:如果您只需要像这样更改某些特定的排序规则使用条件:

    WHERE c.object_id = OBJECT_ID(@table) AND (t.Name LIKE '%char%' OR t.Name LIKE '%text%') 
        AND c.collation_name = 'collation to change'
    

    例如不是AND c.collation_name &lt;&gt; @collate

    就我而言,我对某些列进行了正确/指定的排序规则,并且不想更改它们。

    【讨论】:

      【解决方案7】:

      很抱歉,聚会迟到了,但这是我的 - 为表提供架构和有趣的列和表名。是的,我有一些。

      SELECT 
          'ALTER TABLE [' +  TABLE_SCHEMA + '].[' + TABLE_NAME  
          + '] ALTER COLUMN [' + COLUMN_NAME + '] ' + DATA_TYPE 
          + '(' + CAST(CHARACTER_MAXIMUM_LENGTH AS nvarchar(100)) 
          + ') COLLATE ' + 'Latin1_General_CI_AS' 
          + CASE WHEN IS_NULLABLE = 'YES' THEN ' NULL' ELSE ' NOT NULL' END 
      FROM 
          INFORMATION_SCHEMA.COLUMNS 
      WHERE 
          DATA_TYPE like '%char'
      

      【讨论】:

      • 需要为 CHARACTER_MAXIMUM_LENGTH 添加一个案例来处理 MAX,否则就可以了。
      【解决方案8】:

      以下脚本将与表架构以及 (MAX)、IMAGE 等最新类型一起使用。根据您在此行的需要更改您的排序规则类型 (SET @collat​​e = 'DATABASE_DEFAULT';)

      此处的 SQL 脚本:

      BEGIN
      DECLARE @collate nvarchar(100);
      declare @schema nvarchar(255);
      DECLARE @table nvarchar(255);
      DECLARE @column_name nvarchar(255);
      DECLARE @column_id int;
      DECLARE @data_type nvarchar(255);
      DECLARE @max_length varchar(100);
      DECLARE @row_id int;
      DECLARE @sql nvarchar(max);
      DECLARE @sql_column nvarchar(max);
      
      SET @collate = 'DATABASE_DEFAULT';
      
      DECLARE tbl_cursor CURSOR FOR SELECT (s.[name])schemaName, (o.[name])[tableName]
      FROM sysobjects sy 
      INNER JOIN sys.objects  o on o.name = sy.name
      INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
      WHERE OBJECTPROPERTY(sy.id, N'IsUserTable') = 1
      
      OPEN tbl_cursor FETCH NEXT FROM tbl_cursor INTO @schema,@table
      
      WHILE @@FETCH_STATUS = 0
      BEGIN
          DECLARE tbl_cursor_changed CURSOR FOR
              SELECT ROW_NUMBER() OVER (ORDER BY c.column_id) AS row_id
                  , c.name column_name
                  , t.Name data_type
                  , c.max_length
                  , c.column_id
              FROM sys.columns c
              JOIN sys.types t ON c.system_type_id = t.system_type_id
              LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
              LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
          WHERE c.object_id like OBJECT_ID(@schema+'.'+@table)
          ORDER BY c.column_id
      
      
          OPEN tbl_cursor_changed 
           FETCH NEXT FROM tbl_cursor_changed
          INTO @row_id, @column_name, @data_type, @max_length, @column_id
      
      
      
          WHILE @@FETCH_STATUS = 0
          BEGIN
          IF (@max_length = -1) SET @max_length = 'MAX';
              IF (@data_type LIKE '%char%')
              BEGIN TRY
                  SET @sql = 'ALTER TABLE ' +@schema+'.'+ @table + ' ALTER COLUMN ' + @column_name + ' ' + @data_type + '(' + CAST(@max_length AS nvarchar(100)) + ') COLLATE ' + @collate
                  print @sql
                  EXEC sp_executesql @sql
              END TRY
              BEGIN CATCH
                PRINT 'ERROR:'
                PRINT @sql
              END CATCH
      
              FETCH NEXT FROM tbl_cursor_changed
              INTO @row_id, @column_name, @data_type, @max_length, @column_id
      
          END
      
          CLOSE tbl_cursor_changed
          DEALLOCATE tbl_cursor_changed
      
          FETCH NEXT FROM tbl_cursor
          INTO @schema, @table
      
      END
      
      CLOSE tbl_cursor
      DEALLOCATE tbl_cursor
      
      PRINT 'Collation For All Tables Done!'
      END
      

      【讨论】:

      • 虽然这个处理模式很好,但请注意它不像这里的其他一些处理那样处理 NULL 与 NOT NULL。
      【解决方案9】:

      我总是喜欢纯 SQL,所以:

      SELECT 'ALTER TABLE [' + l.schema_n + '].[' 
             + l.table_name + '] ALTER COLUMN [' 
             + l.column_name + '] ' + l.data_type + '(' 
             + Cast(l.new_max_length AS NVARCHAR(100)) 
             + ') COLLATE ' + l.dest_collation_name + ';', 
             l.schema_n, 
             l.table_name, 
             l.column_name, 
             l.data_type, 
             l.max_length, 
             l.collation_name 
      FROM   (SELECT Row_number() 
                       OVER ( 
                         ORDER BY c.column_id) AS row_id, 
                     Schema_name(o.schema_id)  schema_n, 
                     ta.NAME                   table_name, 
                     c.NAME                    column_name, 
                     t.NAME                    data_type, 
                     c.max_length, 
                     CASE 
                       WHEN c.max_length = -1 
                             OR ( c.max_length > 4000 ) THEN 4000 
                       ELSE c.max_length 
                     END                       new_max_length, 
                     c.column_id, 
                     c.collation_name, 
                     'French_CI_AS'            dest_collation_name 
              FROM   sys.columns c 
                     INNER JOIN sys.tables ta 
                             ON c.object_id = ta.object_id 
                     INNER JOIN sys.objects o 
                             ON c.object_id = o.object_id 
                     JOIN sys.types t 
                       ON c.system_type_id = t.system_type_id 
                     LEFT OUTER JOIN sys.index_columns ic 
                                  ON ic.object_id = c.object_id 
                                     AND ic.column_id = c.column_id 
                     LEFT OUTER JOIN sys.indexes i 
                                  ON ic.object_id = i.object_id 
                                     AND ic.index_id = i.index_id 
              WHERE  1 = 1 
                     AND c.collation_name = 'SQL_Latin1_General_CP1_CI_AS' 
             --'French_CI_AS'-- ALTER DONE YET OLD VALUE :'SQL_Latin1_General_CP1_CI_AS' 
             ) l 
      ORDER  BY l.column_id;
      

      【讨论】:

        【解决方案10】:

        使用上面基于游标的变体作为起点,下面的脚本将输出一组 UPDATE 语句以设置为 DATABASE_DEFAULT,它实际上不会执行 UPDATES

        它支持模式、完整的字符和文本类型集并保留现有的 NULL / NOT NULL。

        我计划使用输出来查找在较低环境中失败的语句,然后手动调整生成的脚本以根据需要删除和重新创建约束。

        DECLARE @collate nvarchar(100);
        DECLARE @schema nvarchar(255);
        DECLARE @table nvarchar(255);
        DECLARE @column_name nvarchar(255);
        DECLARE @column_id int;
        DECLARE @data_type nvarchar(255);
        DECLARE @max_length int;
        DECLARE @max_length_str nvarchar(100);
        DECLARE @is_nullable bit;
        DECLARE @row_id int;
        DECLARE @sql nvarchar(max);
        DECLARE @sql_column nvarchar(max);
        
        SET @collate = 'DATABASE_DEFAULT';
        
        DECLARE local_table_cursor CURSOR FOR
        
        SELECT (s.[name])schemaName, (o.[name])[tableName]
        FROM sysobjects sy 
        INNER JOIN sys.objects  o on o.name = sy.name
        INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
        WHERE OBJECTPROPERTY(sy.id, N'IsUserTable') = 1
        ORDER BY s.[name], o.[name]
        
        OPEN local_table_cursor FETCH NEXT FROM local_table_cursor INTO @schema,@table
        
        WHILE @@FETCH_STATUS = 0
        BEGIN
            DECLARE local_change_cursor CURSOR FOR
        
            SELECT ROW_NUMBER() OVER (ORDER BY c.column_id) AS row_id
                , c.name column_name
                , t.Name data_type
                , col.CHARACTER_MAXIMUM_LENGTH
                , c.column_id
                , c.is_nullable
            FROM sys.columns c
            JOIN sys.types t ON c.system_type_id = t.system_type_id
            JOIN INFORMATION_SCHEMA.COLUMNS col on col.COLUMN_NAME = c.name and c.object_id = OBJECT_ID(col.TABLE_NAME)
            LEFT OUTER JOIN sys.index_columns ic ON ic.object_id = c.object_id AND ic.column_id = c.column_id
            LEFT OUTER JOIN sys.indexes i ON ic.object_id = i.object_id AND ic.index_id = i.index_id
            WHERE c.object_id = OBJECT_ID(@schema+'.'+@table) AND (t.Name LIKE '%char%' OR t.Name LIKE '%text%') 
            ORDER BY c.column_id
        
            OPEN local_change_cursor
            FETCH NEXT FROM local_change_cursor
            INTO @row_id, @column_name, @data_type, @max_length, @column_id, @is_nullable
        
            WHILE @@FETCH_STATUS = 0
            BEGIN
        
                SET @max_length_str = @max_length
                IF (@max_length = -1) SET @max_length_str = 'max'
                IF (@max_length > 4000) SET @max_length_str = '4000'
        
                SET @sql =
                CASE 
                    WHEN @data_type like '%text%' 
                    THEN 'ALTER TABLE [' + @schema+ '].['+ @table + '] ALTER COLUMN [' + @column_name + '] ' + @data_type + ' COLLATE ' + @collate + ' ' + CASE WHEN @is_nullable = 0 THEN 'NOT NULL' ELSE 'NULL' END
                    ELSE 'ALTER TABLE [' + @schema+ '].['+ @table + '] ALTER COLUMN [' + @column_name + '] ' + @data_type + '(' + @max_length_str + ') COLLATE ' + @collate + ' ' + CASE WHEN @is_nullable = 0 THEN 'NOT NULL' ELSE 'NULL' END
                END
                PRINT @sql
        
                FETCH NEXT FROM local_change_cursor
                INTO @row_id, @column_name, @data_type, @max_length, @column_id, @is_nullable
        
            END
        
            CLOSE local_change_cursor
            DEALLOCATE local_change_cursor
        
            FETCH NEXT FROM local_table_cursor
            INTO @schema, @table
        END
        
        CLOSE local_table_cursor
        DEALLOCATE local_table_cursor
        GO
        

        【讨论】:

        【解决方案11】:

        更改包含所有字段的数据库排序规则的最简单方法是合并复制:

        • 使用目标排序规则(服务器排序规则)获取服务器
        • 在旧服务器上创建合并发布
        • 将源数据库的所有产品添加到发布中
        • 运行 snapsoht 代理并等待它完成
        • 将推送订阅添加到您的出版物,以使用良好排序规则的服务器为目标
        • 初始化订阅
        • 签入复制监视器并等待代理准备就绪
        • 删除订阅
        • 删除出版物

        以下 sql scipt 为您的用户表创建合并发布 我在 Managemet Studio 中执行的其他步骤,以及在后续步骤中使用单独的脚本创建脚本对象(如存储过程、视图等)。

        ALTER PROCEDURE [dbo].[CreateMergePublication]
            @PublicationName nvarchar(max) = N'Pubi'
        AS BEGIN
        SET NOCOUNT ON
            BEGIN TRY
        
        
                -- *** BEGIN BLL ***
        
        
                declare @DBName nvarchar(max) 
                select top 1 @DBName = TABLE_CATALOG from INFORMATION_SCHEMA.TABLES
                exec sp_replicationdboption @dbname = @DBName, @optname = N'merge publish', @value = N'true'
        
                -- Mergeveröffentlichung wird hinzugefügt
                declare @desc nvarchar(max) = N'Mergeveröffentlichung der ' + @dbname + '-Datenbank von Verleger ' + @@SERVERNAME
                exec sp_addmergepublication 
                    @publication = @PublicationName, 
                    @description = @desc , 
                    @sync_mode = N'native', 
                    @retention = 14, 
                    @allow_push = N'true', 
                    @allow_pull = N'true', 
                    @allow_anonymous = N'true', 
                    @enabled_for_internet = N'false', 
                    @snapshot_in_defaultfolder = N'true', 
                    @compress_snapshot = N'false', 
                    @ftp_port = 21, 
                    @ftp_subdirectory = N'ftp', 
                    @ftp_login = N'anonymous', 
                    @allow_subscription_copy = N'false', 
                    @add_to_active_directory = N'false', 
                    @dynamic_filters = N'false', 
                    @conflict_retention = 14, 
                    @keep_partition_changes = N'false', 
                    @allow_synctoalternate = N'false', 
                    @max_concurrent_merge = 0, 
                    @max_concurrent_dynamic_snapshots = 0, 
                    @use_partition_groups = null, 
                    @publication_compatibility_level = N'100RTM', 
                    @replicate_ddl = 1, 
                    @allow_subscriber_initiated_snapshot = N'false', 
                    @allow_web_synchronization = N'false', 
                    @allow_partition_realignment = N'true', 
                    @retention_period_unit = N'days', 
                    @conflict_logging = N'both', 
                    @automatic_reinitialization_policy = 0
        
        
                exec sp_addpublication_snapshot 
                    @publication = @PublicationName, 
                    @frequency_type = 4, 
                    @frequency_interval = 14, 
                    @frequency_relative_interval = 1, 
                    @frequency_recurrence_factor = 0, 
                    @frequency_subday = 1, 
                    @frequency_subday_interval = 5, 
                    @active_start_time_of_day = 500, 
                    @active_end_time_of_day = 235959, 
                    @active_start_date = 0, 
                    @active_end_date = 0, 
                    @job_login = null, 
                    @job_password = null, 
                    @publisher_security_mode = 1
        
                declare @schema nvarchar(max), @table nvarchar(max), @uniquename nvarchar(max)
                declare cr cursor for
                    select TABLE_SCHEMA, TABLE_NAME from INFORMATION_SCHEMA.TABLES 
                    where TABLE_TYPE = 'BASE TABLE' and TABLE_NAME not like 'sys%' and TABLE_NAME not like 'ms%' and TABLE_NAME not like 'dtprop%'
                    order by TABLE_NAME
                open cr
                WHILE 1=1 BEGIN
                    FETCH cr INTO @schema, @table
                    IF @@FETCH_STATUS <> 0 BREAK
                    set @uniquename = @schema + @table
        
                    print @schema + '.' + @table + ' (' + @uniquename + ')'
                    exec sp_addmergearticle 
                        @publication = @PublicationName, 
                        @article = @uniquename, 
                        @source_owner = @schema, 
                        @source_object = @table, 
                        @type = N'table', 
                        @description = N'', 
                        @creation_script = null, 
                        @pre_creation_cmd = N'none', 
                        @schema_option = 0x000000010C034FD1, 
                        @identityrangemanagementoption = N'manual', 
                        @destination_owner = @schema, 
                        @force_reinit_subscription = 1, 
                        @column_tracking = N'false', 
                        @subset_filterclause = N'', 
                        @vertical_partition = N'false', 
                        @verify_resolver_signature = 1, 
                        @allow_interactive_resolver = N'false', 
                        @fast_multicol_updateproc = N'true', 
                        @check_permissions = 0, 
                        @subscriber_upload_options = 0, 
                        @delete_tracking = N'true', 
                        @compensate_for_errors = N'false', 
                        @stream_blob_columns = N'false', 
                        @partition_options = 0
        
                END 
        
                close cr
                deallocate cr
        
        
        
        
        
                -- *** END BLL ***
        
            END TRY
            BEGIN CATCH
                IF CURSOR_STATUS('global','cr') >= 0
                BEGIN 
                    close cr
                    deallocate cr
                END
        
                DECLARE @ErrMsg nvarchar(4000), @ErrSeverity INT, @ErrorState INT;
                SELECT @ErrMsg = ERROR_MESSAGE(),@ErrSeverity = ERROR_SEVERITY(),@ErrorState = ERROR_STATE()
                RAISERROR(@ErrMsg, @ErrSeverity, @ErrorState)
        
        
            END CATCH;
        
        END
        

        【讨论】:

        • 是否有一个 SQL 脚本可以解释您说明的流程?
        • 查看我的编辑以创建合并出版物。我通常在 Management Studio 中手动执行的其他步骤。
        【解决方案12】:

        这个答案可能有点晚,但您也可以生成语句。这将比使用 CURSOR 快得多。

        select 
        'ALTER TABLE '+ tb.TABLE_NAME + ' ALTER COLUMN '+ cl.COLUMN_NAME + ' ' + DATA_TYPE +'('+ cast(CHARACTER_MAXIMUM_LENGTH as nchar(3)) + ') ' + 'COLLATE Latin1_General_CI_AS '
        FROM [DATABASE].INFORMATION_SCHEMA.columns cl  
        left join [DATABASE].tables tb on tb.TABLE_NAME = cl.TABLE_NAME and tb.Table_Schema=cl.table_schema 
        left join  [DATABASE].INFORMATION_SCHEMA.KEY_COLUMN_USAGE kc on kc.table_name = tb.table_name and kc.TABLE_SCHEMA = cl.TABLE_SCHEMA  and kc.column_name=cl.COLUMN_NAME
        WHERE DATA_TYPE in ('nvarchar','nchar')
        

        希望这可以帮助某人。

        【讨论】:

          猜你喜欢
          • 2019-05-12
          • 1970-01-01
          • 2011-03-28
          • 2016-11-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-05-19
          • 2015-04-03
          相关资源
          最近更新 更多