Массовое изменение уникальных идентификаторов в SQL Server - PullRequest
0 голосов
/ 13 февраля 2009

[Это немного необычная проблема, я знаю ...]

Мне нужен скрипт, который изменит каждое уникальное значение идентификатора на новое в нашей базе данных. Проблема заключается в том, что у нас есть таблицы конфигурации, которые можно экспортировать между экземплярами нашего программного обеспечения, которые чувствительны к идентификатору (забивая существующие идентификаторы). Несколько лет назад мы установили «достаточно широкий» разрыв между идентификаторами «стандартной конфигурации» разработки и экземплярами нашего клиента, который теперь , а не достаточно широк :( - например, мы получаем конфликты идентификаторов, когда клиенты импортировать нашу стандартную конфигурацию.

SQL-скрипт, выполняющий следующие действия, определенно является самым простым / кратчайшим периодом, который мы можем сделать. например исправление кода слишком сложно и подвержено ошибкам. Обратите внимание, что мы не "устраняем" проблему здесь. Просто изменив разрыв с 1000 до 1000000 или более (существующий разрыв занял 5 лет).

Я считаю, что самое простое решение было бы:

  • изменить все наши таблицы на UPDATE_CASCADE (ни одна из них - это значительно упростит сценарий)
  • создать таблицу идентификаторов с новым младшим идентификатором, который нам нужен
  • Для каждой таблицы измените идентификатор на следующий в таблице идентификаторов (при необходимости используйте флаги модификатора вставки идентификатора). Возможно, после обработки каждой таблицы мы могли бы сбросить таблицу идентификаторов.
  • выключите UPDATE_CASCADE и удалите таблицу идентификаторов.

У кого-нибудь есть сценарии для этого (или частичные сценарии?). Я опишу что-нибудь полезное:).

Ответы [ 3 ]

1 голос
/ 13 февраля 2009

К сожалению, UPDATE_CASCADE не существует в мире Sql Server. Я предлагаю для каждой таблицы повторно вводить следующее: (псевдокод)

BACKUP DATABASE
CHECK BACKUP WORKS!

FOR EACH TABLE TO BE RE-KEYED
   DROP ALL FOREIGN KEY CONSTRAINTS, INDEXES ETC FROM TABLE

   SELECT ID + Number, ALL_OTHER_FIELDS INTO TEMP_TABLE FROM TABLE
   RENAME TABLE OLD_TABLE
   RENAME TEMP_TABLE TABLE

   FOR ALL TABLES REFERENCING THIS TABLE
       UPDATE FOREIGN_KEY_TABLE SET FK_ID = FK_ID + new number
   END FOR

   RE-APPLY FOREIGN KEY CONSTRAINTS, INDEXES ETC FROM TABLE

END FOR

Проверьте, все ли еще работает ...

Этот процесс может быть автоматизирован с помощью объектов DMO / SMO, но в зависимости от числа задействованных таблиц я бы сказал, что использование Management Studio для генерации сценариев, которые затем можно редактировать, возможно, быстрее. В конце концов, вам нужно делать это только один раз / 5 лет.

0 голосов
/ 13 февраля 2009

Здесь мы рассмотрим код для SQL 2005. Он огромный, хакерский, но он будет работать (за исключением случая, когда у вас есть первичный ключ, который представляет собой смесь двух других первичных ключей) .

Если кто-то может переписать это с более быстрым добавлением идентификатора MrTelly (что не потребует построения sql из курсора для каждой обновленной строки), тогда я отмечу это как принятый ответ. (Если я не замечу новый ответ, проголосуйте за это - тогда я замечу:))

BEGIN TRAN
SET NOCOUNT ON;

DECLARE @newLowId INT
SET @newLowId = 1000000

DECLARE @sql VARCHAR(4000)

--**** SELECT ALL TABLES WITH IDENTITY COLUMNS ****
DECLARE tables  SCROLL CURSOR
FOR 
SELECT '[' + SCHEMA_NAME(schema_id) + '].[' + t.name + ']', c.name
FROM sys.identity_columns c
INNER JOIN sys.objects t
    on c.object_id = t.object_id
WHERE t.type_Desc = 'USER_TABLE'

OPEN tables

DECLARE @Table VARCHAR(100)
DECLARE @IdColumn VARCHAR(100)

CREATE Table #IdTable(
  id INT IDENTITY(1,1),
  s CHAR(1)
)

FETCH FIRST FROM tables
INTO @Table, @IdColumn

WHILE @@FETCH_STATUS = 0
BEGIN
    PRINT('
****************** '+@Table+' ******************
')
    --Reset the idtable to the 'low' id mark - remove this line if you want all records to have distinct ids across the database
    DELETE FROM #IdTable
    DBCC CHECKIDENT('#IdTable', RESEED, @newLowId)

    --**** GENERATE COLUMN SQL (for inserts and deletes - updating identities is not allowed) ****
    DECLARE tableColumns CURSOR FOR
        SELECT column_name FROM information_schema.columns
        WHERE '[' + table_schema + '].[' + table_name + ']' = @Table
        AND column_name <> @IdColumn
    OPEN tableColumns
    DECLARE @columnName VARCHAR(100)
    DECLARE @columns VARCHAR(4000)
    SET @columns = ''
    FETCH NEXT FROM tableColumns INTO @columnName
    WHILE @@FETCH_STATUS = 0
    BEGIN
        SET @columns = @columns + @columnName
        FETCH NEXT FROM tableColumns INTO @columnName
        IF @@FETCH_STATUS = 0 SET @columns = @columns + ', '
    END

    CLOSE tableColumns
    DEALLOCATE tableColumns

    --**** GENERATE FOREIGN ROW UPDATE SQL ****
    DECLARE foreignkeys SCROLL CURSOR
    FOR 
    SELECT con.name, 
        '[' + SCHEMA_NAME(f.schema_id) + '].[' + f.name + ']' fTable, fc.column_name , 
        '[' + SCHEMA_NAME(p.schema_id) + '].[' + p.name + ']' pTable,  pc.column_name 
    FROM sys.foreign_keys con
    INNER JOIN sysforeignkeys syscon
        ON con.object_id = syscon.constid
    INNER JOIN sys.objects f
        ON con.parent_object_id = f.object_id
    INNER JOIN information_schema.columns fc
        ON fc.table_schema = SCHEMA_NAME(f.schema_id)
        AND fc.table_name = f.name
        AND fc.ordinal_position = syscon.fkey

    INNER JOIN sys.objects p
        ON con.referenced_object_id = p.object_id
    INNER JOIN information_schema.columns pc
        ON pc.table_schema = SCHEMA_NAME(p.schema_id)
        AND pc.table_name = p.name
        AND pc.ordinal_position = syscon.rkey
    WHERE '[' + SCHEMA_NAME(p.schema_id) + '].[' + p.name + ']' = @Table

    OPEN foreignkeys

    DECLARE @FKeyName VARCHAR(100)
    DECLARE @FTable VARCHAR(100)
    DECLARE @FColumn VARCHAR(100)
    DECLARE @PTable VARCHAR(100)
    DECLARE @PColumn VARCHAR(100)

    --**** RE-WRITE ALL IDS IN THE TABLE ****
    SET @sql='DECLARE tablerows CURSOR FOR
    SELECT CAST('+@IdColumn+' AS VARCHAR) FROM '+@Table+' ORDER BY '+@IdColumn
    PRINT(@sql)
    exec(@sql)

    OPEN tablerows
    DECLARE @rowid VARCHAR(100)
    DECLARE @id VARCHAR(100)


    FETCH NEXT FROM tablerows INTO @rowid
    WHILE @@FETCH_STATUS = 0
    BEGIN
        --generate new id
        INSERT INTO #IdTable VALUES ('')
        SELECT @id = CAST(@@IDENTITY AS VARCHAR)
        IF @rowId <> @Id
        BEGIN
            PRINT('Modifying '+@Table+': changing '+@rowId+' to '+@id)
            SET @sql='SET IDENTITY_INSERT ' + @Table + ' ON
    INSERT INTO '+@Table+' ('+@IdColumn+','+@columns+') SELECT '+@id+','+@columns+' FROM '+@Table+' WHERE '+@IdColumn+'='+@rowId

            --Updating all foreign rows...
            FETCH FIRST FROM foreignkeys
            INTO @FKeyName, @FTable, @FColumn, @PTable, @PColumn

            WHILE @@FETCH_STATUS = 0
            BEGIN
                SET @sql = @sql + '
    UPDATE '+@FTable+' SET '+@FColumn+'='+@id+' WHERE '+@FColumn+' ='+@rowId
                FETCH NEXT FROM foreignkeys
                INTO @FKeyName, @FTable, @FColumn, @PTable, @PColumn
            END
            SET @sql=@sql + '
    DELETE FROM '+@Table+' WHERE '+@IdColumn+'='+@rowId

            PRINT(@sql)
            exec(@sql)
        END
        FETCH NEXT FROM tablerows INTO @rowid
    END

    CLOSE tablerows
    DEALLOCATE tablerows
    CLOSE foreignkeys
    DEALLOCATE foreignkeys

    --Revert to normal identity operation - update the identity to the latest id...
    DBCC CHECKIDENT(@Table, RESEED, @@IDENTITY)
    SET @sql='SET IDENTITY_INSERT ' + @Table + ' OFF'
    PRINT(@sql)
    exec(@sql)

    FETCH NEXT FROM tables
    INTO @Table, @IdColumn
END

CLOSE tables
DEALLOCATE tables

DROP TABLE #IdTable
--COMMIT
--ROLLBACK
0 голосов
/ 13 февраля 2009

Почему вы не используете отрицательные числа для стандартных значений конфигурации и продолжаете использовать положительные числа для других вещей?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...