Как обновить первичный ключ

вот моя проблема: У меня есть 2 таблицы:

  1. рабочий, со столбцами |ID|OTHER_STAF| , где ID-первичный ключ
  2. фирма, с колонками |FPK|ID|SOMETHING_ELSE|, где комбинация FPK и ID делает первичный ключ, а также ID является внешним ключом, на который ссылается WORKER.ID (не null и должно иметь то же значение, что и в WORKER).

Я хочу сделать хранимую процедуру UPDATE_ID_WORKER, где я хотел бы изменить значение конкретного идентификатора в WORKER, а также во всех случаях удельное значение ID в фирме.

хранимая процедура:

........ @идентификатор .. ???? ........

6 ответов


вы не должны этого делать, но вместо этого вставьте новую запись и обновите ее таким образом.
Но, если вам действительно нужно, вы можете сделать следующее:

  • временно отключить принудительное применение ограничений FK (например,ALTER TABLE foo WITH NOCHECK CONSTRAINT ALL)
  • затем обновите свой ПК
  • затем обновите FKs, чтобы соответствовать изменению PK
  • наконец, включите принудительное выполнение ограничений FK

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

  1. для этой проблемы не имеет значения, является ли ключ реляционным ключом ("составленным из данных") и, следовательно, имеет реляционную целостность, мощность и скорость, или если" ключ " является идентификатором записи, без этой реляционной целостности, мощности и скорости. Эффект тот же.

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

  3. дело в том, что идентификатор ключа или записи переносится туда, где требуется ссылка.

во-вторых, если вам нужно изменить значение ключа или ID записи, Ну, вы должны изменить его. Вот OLTP-совместимый со стандартом метод. Отмечать что поставщики высокого класса не разрешают "каскадное обновление".

  • написать proc. Foo_UpdateCascade_tr @ID, где Foo-имя таблицы

  • начать транзакцию

  • первая вставка-выберите новую строку в родительской таблице из старой строки с новым ключом или значением RID

  • во-вторых, для всех дочерних таблиц, работающих сверху вниз, вставить-выберите новые строки из старых строк с новым ключом или Значение RID

  • В-третьих, удалите строки в дочерних таблицах, которые имеют старый ключ или значение RID, работая снизу вверх

  • наконец, удалите строку в родительской таблице, которая имеет старый ключ или значение RID

  • совершение сделки

Re другие ответы

другие ответы неверны.

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

  • необходимость изменения значения ключа или МПОГ не указывает на дефект конструкции. Это обычная потребность. Это смягчается путем выбора стабильных (не статических) ключей. Его можно смягчить, но нельзя устранить.

  • A суррогат, заменяющий естественный ключ, не будет иметь никакого значения. В приведенном примере "ключ" является суррогатом. И его нужно обновить.

    • пожалуйста, просто суррогат, нет такого понятия как "суррогатный ключ", потому что каждое слово противоречит другому. Либо это ключ (составленный из данных), либо нет. Суррогат не состоит из данных, он явно не. У нее нет никаких свойств Ключ.
  • нет ничего "сложного" в каскадировании всех необходимых изменений. См. шаги, приведенные выше.

  • нет ничего, что можно предотвратить, если Вселенная меняется. Она меняется. Справиться с этим. И поскольку база данных-это набор фактов о Вселенной, когда Вселенная изменяется, база данных должна измениться. То есть жизнь в большом городе, она не для новых игроки.

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

    • имена, описания и т. д. Существуют один раз, в одной строке. Ключи существуют везде, где они были перенесены. И если "ключ" - это Рид, то и Рид тоже, существует везде, где она была перенесена.
  • не обновляйте ПК! это вторая самая веселая вещь, которую я прочитал за последнее время. Добавить новый столбец - это большинство.


Если вы конечно это изменение подходит для среды, в которой вы работаете: установите условия FK во вторичных таблицах для обновления каскадирования.

например, при использовании SSMS в качестве GUI:

  1. щелкните правой кнопкой мыши по клавише
  2. выберите изменить
  3. сложите "вставить и обновить конкретные"
  4. для "Правила обновления" выберите Каскад.
  5. закройте диалоговое окно и сохраните ключ.

когда вы затем обновите значение в столбце PK в основной таблице, ссылки FK в других таблицах будут обновлены, чтобы указать на новое значение, сохраняя целостность данных.


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

сложно каскадировать все необходимые изменения внешних ключей. Рекомендуется никогда не обновлять первичный ключ, и если вы считаете это необходимым, вы должны использовать Surrogate Primary Key, который является ключом, не производным от данных приложения. В результате его значение не связано с бизнес-логикой и никогда не должно меняться (и должно быть невидимый для конечного пользователя). Затем можно обновить и отобразить другой столбец.

например:

BadUserTable
UserID     varchar(20) primary key --user last name
other columns...

когда вы создаете много таблиц, которые имеют FK для UserID, чтобы отслеживать все, над чем работал пользователь, но этот пользователь затем женится и хочет, чтобы идентификатор соответствовал их новой фамилии, вам не повезло.

GoodUserTable
UserID    int identity(1,1) primary key
UserLogin varchar(20) 
other columns....

Теперь вы FK суррогатный первичный ключ ко всем другим таблицам и отображаете UserLogin, когда это необходимо, позвольте им войти в систему, используя это значение, и когда им нужно изменить его, вы изменяете его в одном столбце только одной строки.


не обновляйте первичный ключ. Это может вызвать много проблем для вас держать ваши данные нетронутыми, если у вас есть какие-либо другие таблицы ссылаются на нее.

В идеале, если вы хотите уникальное поле, которое можно обновить, создайте новое поле.


вы можете использовать эту рекурсивную функцию для создания необходимого сценария T-SQL.

CREATE FUNCTION dbo.Update_Delete_PrimaryKey
(
    @TableName      NVARCHAR(255),
    @ColumnName     NVARCHAR(255),
    @OldValue       NVARCHAR(MAX),
    @NewValue       NVARCHAR(MAX),
    @Del            BIT
)
RETURNS NVARCHAR 
(
    MAX
)
AS
BEGIN
    DECLARE @fks TABLE 
            (
                constraint_name NVARCHAR(255),
                table_name NVARCHAR(255),
                col NVARCHAR(255)
            );
    DECLARE @Sql                  NVARCHAR(MAX),
            @EnableConstraints     NVARCHAR(MAX);

    SET @Sql = '';
    SET @EnableConstraints = '';

    INSERT INTO @fks
      (
        constraint_name,
        table_name,
        col
      )
    SELECT oConstraint.name     constraint_name,
           oParent.name         table_name,
           oParentCol.name      col
    FROM   sys.foreign_key_columns sfkc
           --INNER JOIN sys.foreign_keys sfk
           --     ON  sfk.[object_id] = sfkc.constraint_object_id

           INNER JOIN sys.sysobjects oConstraint
                ON  sfkc.constraint_object_id = oConstraint.id
           INNER JOIN sys.sysobjects oParent
                ON  sfkc.parent_object_id = oParent.id
           INNER JOIN sys.all_columns oParentCol
                ON  sfkc.parent_object_id = oParentCol.object_id
                AND sfkc.parent_column_id = oParentCol.column_id
           INNER JOIN sys.sysobjects oReference
                ON  sfkc.referenced_object_id = oReference.id
           INNER JOIN sys.all_columns oReferenceCol
                ON  sfkc.referenced_object_id = oReferenceCol.object_id
                AND sfkc.referenced_column_id = oReferenceCol.column_id
    WHERE  oReference.name = @TableName
           AND oReferenceCol.name = @ColumnName
    --AND (@Del <> 1 OR sfk.delete_referential_action = 0)
    --AND (@Del = 1 OR sfk.update_referential_action = 0)

    IF EXISTS(
           SELECT 1
           FROM   @fks
       )
    BEGIN
        DECLARE @Constraint     NVARCHAR(255),
                @Table          NVARCHAR(255),
                @Col            NVARCHAR(255)  

        DECLARE Table_Cursor CURSOR LOCAL 
        FOR
            SELECT f.constraint_name,
                   f.table_name,
                   f.col
            FROM   @fks AS f

        OPEN Table_Cursor FETCH NEXT FROM Table_Cursor INTO @Constraint, @Table,@Col  
        WHILE (@@FETCH_STATUS = 0)
        BEGIN
            IF @Del <> 1
            BEGIN
                SET @Sql = @Sql + 'ALTER TABLE ' + @Table + ' NOCHECK CONSTRAINT ' + @Constraint + CHAR(13) + CHAR(10);
                SET @EnableConstraints = @EnableConstraints + 'ALTER TABLE ' + @Table + ' CHECK CONSTRAINT ' + @Constraint 
                    + CHAR(13) + CHAR(10);
            END

            SET @Sql = @Sql + dbo.Update_Delete_PrimaryKey(@Table, @Col, @OldValue, @NewValue, @Del);
            FETCH NEXT FROM Table_Cursor INTO @Constraint, @Table,@Col
        END

        CLOSE Table_Cursor DEALLOCATE Table_Cursor
    END

    DECLARE @DataType NVARCHAR(30);
    SELECT @DataType = t.name +
           CASE 
                WHEN t.name IN ('char', 'varchar', 'nchar', 'nvarchar') THEN '(' +
                     CASE 
                          WHEN c.max_length = -1 THEN 'MAX'
                          ELSE CONVERT(
                                   VARCHAR(4),
                                   CASE 
                                        WHEN t.name IN ('nchar', 'nvarchar') THEN c.max_length / 2
                                        ELSE c.max_length
                                   END
                               )
                     END + ')'
                WHEN t.name IN ('decimal', 'numeric') THEN '(' + CONVERT(VARCHAR(4), c.precision) + ',' 
                     + CONVERT(VARCHAR(4), c.Scale) + ')'
                ELSE ''
           END
    FROM   sys.columns c
           INNER JOIN sys.types t
                ON  c.user_type_id = t.user_type_id
    WHERE  c.object_id = OBJECT_ID(@TableName)
           AND c.name = @ColumnName

    IF @Del <> 1
    BEGIN
        SET @Sql = @Sql + 'UPDATE [' + @TableName + '] SET [' + @ColumnName + '] = CONVERT(' + @DataType + ', ' + ISNULL('N''' + @NewValue + '''', 'NULL') 
            + ') WHERE [' + @ColumnName + '] = CONVERT(' + @DataType + ', ' + ISNULL('N''' + @OldValue + '''', 'NULL') +
            ');' + CHAR(13) + CHAR(10);
        SET @Sql = @Sql + @EnableConstraints;
    END
    ELSE
        SET @Sql = @Sql + 'DELETE [' + @TableName + '] WHERE [' + @ColumnName + '] = CONVERT(' + @DataType + ', N''' + @OldValue 
            + ''');' + CHAR(13) + CHAR(10);
    RETURN @Sql;
END
GO

DECLARE @Result NVARCHAR(MAX);
SET @Result = dbo.Update_Delete_PrimaryKey('@TableName', '@ColumnName', '@OldValue', '@NewValue', 0);/*Update*/
EXEC (@Result)
SET @Result = dbo.Update_Delete_PrimaryKey('@TableName', '@ColumnName', '@OldValue', NULL, 1);/*Delete*/
EXEC (@Result)
GO

DROP FUNCTION Update_Delete_PrimaryKey;