Как обновить первичный ключ
вот моя проблема: У меня есть 2 таблицы:
- рабочий, со столбцами
|ID|OTHER_STAF|
, где ID-первичный ключ - фирма, с колонками
|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
во-первых, мы выбираем стабильные (не статические) столбцы данных для формирования первичного ключа, именно потому, что обновление ключей в реляционной базе данных (в которой ссылки по ключу) - это то, чего мы хотим избежать.
для этой проблемы не имеет значения, является ли ключ реляционным ключом ("составленным из данных") и, следовательно, имеет реляционную целостность, мощность и скорость, или если" ключ " является идентификатором записи, без этой реляционной целостности, мощности и скорости. Эффект тот же.
Я заявляю это, потому что есть много сообщений невежественных, которые предполагают, что это точная причина того, что идентификаторы записей каким-то образом лучше, чем реляционные ключи.
дело в том, что идентификатор ключа или записи переносится туда, где требуется ссылка.
во-вторых, если вам нужно изменить значение ключа или ID записи, Ну, вы должны изменить его. Вот OLTP-совместимый со стандартом метод. Отмечать что поставщики высокого класса не разрешают "каскадное обновление".
написать proc. Foo_UpdateCascade_tr @ID, где Foo-имя таблицы
начать транзакцию
первая вставка-выберите новую строку в родительской таблице из старой строки с новым ключом или значением RID
во-вторых, для всех дочерних таблиц, работающих сверху вниз, вставить-выберите новые строки из старых строк с новым ключом или Значение RID
В-третьих, удалите строки в дочерних таблицах, которые имеют старый ключ или значение RID, работая снизу вверх
наконец, удалите строку в родительской таблице, которая имеет старый ключ или значение RID
совершение сделки
Re другие ответы
другие ответы неверны.
отключение ограничений, а затем их включение после Обновление требуемых строк (родитель плюс все дети) - это не то, что человек будет делать в онлайн-среде, если он хочет остаться на работе. Этот совет полезен для однопользовательских баз данных.
необходимость изменения значения ключа или МПОГ не указывает на дефект конструкции. Это обычная потребность. Это смягчается путем выбора стабильных (не статических) ключей. Его можно смягчить, но нельзя устранить.
-
A суррогат, заменяющий естественный ключ, не будет иметь никакого значения. В приведенном примере "ключ" является суррогатом. И его нужно обновить.
- пожалуйста, просто суррогат, нет такого понятия как "суррогатный ключ", потому что каждое слово противоречит другому. Либо это ключ (составленный из данных), либо нет. Суррогат не состоит из данных, он явно не. У нее нет никаких свойств Ключ.
нет ничего "сложного" в каскадировании всех необходимых изменений. См. шаги, приведенные выше.
нет ничего, что можно предотвратить, если Вселенная меняется. Она меняется. Справиться с этим. И поскольку база данных-это набор фактов о Вселенной, когда Вселенная изменяется, база данных должна измениться. То есть жизнь в большом городе, она не для новых игроки.
-
люди женятся и ежи хоронят не проблема (несмотря на такие примеры используются, чтобы предположить, что это is проблема). Потому что мы не используем имена, как ключи. Мы используем небольшие стабильные идентификаторы, которые используются для идентификации данных во Вселенной.
- имена, описания и т. д. Существуют один раз, в одной строке. Ключи существуют везде, где они были перенесены. И если "ключ" - это Рид, то и Рид тоже, существует везде, где она была перенесена.
не обновляйте ПК! это вторая самая веселая вещь, которую я прочитал за последнее время. Добавить новый столбец - это большинство.
Если вы конечно это изменение подходит для среды, в которой вы работаете: установите условия FK во вторичных таблицах для обновления каскадирования.
например, при использовании SSMS в качестве GUI:
- щелкните правой кнопкой мыши по клавише
- выберите изменить
- сложите "вставить и обновить конкретные"
- для "Правила обновления" выберите Каскад.
- закройте диалоговое окно и сохраните ключ.
когда вы затем обновите значение в столбце 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;