Как обновить первичный ключ - PullRequest
19 голосов
/ 23 марта 2010

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

  1. РАБОЧИЙ, со столбцами |ID|OTHER_STAF|, где ID - первичный ключ
  2. ФИРМА, со столбцами |FPK|ID|SOMETHING_ELSE|, гдекомбинация FPK и ID делает первичный ключ, а также ID является внешним ключом, на который ссылается WORKER.ID (не ноль, и должен иметь то же значение, что и в WORKER).

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

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

........ @Я бы .. ????........

Ответы [ 6 ]

30 голосов
/ 23 марта 2010

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

  • Отключитьвременно применяя ограничения FK (например, ALTER TABLE foo WITH NOCHECK CONSTRAINT ALL)
  • Затем обновите свой PK
  • Затем обновите свои FK, чтобы они соответствовали изменению PK
  • Наконец, включите обратное применение ограничений FK
23 голосов
/ 10 августа 2015

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

  1. Для этой проблемы не имеет значения, является ли ключ реляционным ключом («составленным из данных») и, следовательно, имеет реляционную целостность, мощность и скорость, или если «ключ» имеет значение Идентификатор записи, без единой Реляционной Целостности, Мощности и Скорости. Эффект тот же.

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

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

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

  • Написать прок. Foo_UpdateCascade_tr @ID, где Foo - имя таблицы

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

  • Сначала INSERT-SELECT новой строки в родительской таблице, из старой строки, с новым значением Key или RID

  • Во-вторых, для всех дочерних таблиц, работающих сверху вниз, INSERT-ВЫБРАТЬ новые строки из старых строк с новым значением Key или RID

  • В-третьих, УДАЛИТЬ строки в дочерних таблицах, которые имеют старое значение ключа или RID, работая снизу вверх

  • Последнее, УДАЛИТЬ строку в родительской таблице со старым значением Key или RID

  • Совершить транзакцию

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

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

  • Отключение ограничений и последующее их включение после ОБНОВЛЕНИЯ требуемых строк (родительский плюс все дочерние элементы) - это не то, что человек сделал бы в производственной онлайн-среде, если он хочет остаться занятым. Этот совет хорош для однопользовательских баз данных.

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

  • Суррогат, заменяющий естественный Ключ, не будет иметь никакого значения. В приведенном вами примере «ключ» - это суррогат. И это нужно обновить.

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

  • Ничто не может быть предотвращено при изменении вселенной. Меняется. Смирись с этим. А поскольку база данных представляет собой набор фактов о юниверсе, то при изменении юниверса база данных должна будет измениться. Это жизнь в большом городе, это не для новых игроков.

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

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

11 голосов
/ 17 сентября 2015

Если вы уверены , что это изменение подходит для среды, в которой вы работаете: задайте условия FK для вторичных таблиц как ОБНОВЛЕНИЕ КАСКАДИНГА.

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

  1. щелкните правой кнопкой мыши на ключе
  2. выберите Изменить
  3. Развернуть 'ВСТАВИТЬ И ОБНОВИТЬ специфичные'
  4. Для «Обновить правило» выберите Каскад.
  5. Закройте диалоговое окно и сохраните ключ.

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

5 голосов
/ 23 марта 2010

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

Сложно каскадировать все необходимые изменения внешних ключей.Рекомендуется никогда не обновлять первичный ключ, и, если вы сочтете это необходимым, вам следует использовать 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....

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

3 голосов
/ 23 марта 2010

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

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

0 голосов
/ 07 августа 2016

Вы можете использовать эту рекурсивную функцию для генерации необходимого скрипта 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...