Sql server 如何更新主键
这是我的问题-我有两张表:Sql server 如何更新主键,sql-server,sql-server-2008,foreign-keys,composite-primary-key,Sql Server,Sql Server 2008,Foreign Keys,Composite Primary Key,这是我的问题-我有两张表: WORKER,具有列| ID | OTHER_STAF |,其中ID是主键 FIRM,带有列| FPK | ID | SOMETHING | |,其中FPK和ID组合构成主键,并且ID是引用WORKER.ID的外键(不为null,并且必须与WORKER中的值相同) 我想让存储过程更新\u ID\u WORKER,在这里我想更改WORKER中特定ID的值,并且在所有实例中更改FIRM中特定ID的值。不要更新主键。如果有任何其他表引用数据,那么它可能会给保持数据完整性带来
| ID | OTHER_STAF |
,其中ID是主键| FPK | ID | SOMETHING | |
,其中FPK和ID组合构成主键,并且ID是引用WORKER.ID的外键(不为null,并且必须与WORKER中的值相同)我想让存储过程
更新\u ID\u WORKER
,在这里我想更改WORKER中特定ID的值,并且在所有实例中更改FIRM中特定ID的值。不要更新主键。如果有任何其他表引用数据,那么它可能会给保持数据完整性带来很多问题
理想情况下,如果您想要一个可更新的唯一字段,请创建一个新字段。您不应该这样做,而应该插入一个新记录,并以这种方式进行更新。
但是,如果确实需要,您可以执行以下操作:
- 暂时禁用强制执行FK约束(例如,
)ALTER TABLE foo WITH NOCHECK CONSTRAINT ALL
- 然后更新你的PK
- 然后更新FKs以匹配PK更改
- 最后启用反向强制FK约束
代理主键
,该主键不是从应用程序数据派生的。因此,它的价值与业务逻辑无关,永远不需要更改(最终用户应该看不见)。然后可以更新和显示其他列
例如:
BadUserTable
UserID varchar(20) primary key --user last name
other columns...
如果您创建了许多表,这些表都有一个FK-to-UserID,用来跟踪用户所做的一切,但是该用户结婚后想要一个与他们的新姓氏匹配的ID,那么您就不走运了
GoodUserTable
UserID int identity(1,1) primary key
UserLogin varchar(20)
other columns....
现在,您将代理主键FK到所有其他表,并在必要时显示UserLogin,允许他们使用该值登录,当他们需要更改时,您只在一行的一列中更改它。首先,我们选择稳定(非静态)数据列来形成主键,正是因为更新关系数据库中的键(其中引用是按键进行的)是我们希望避免的事情
- 写一个程序。Foo_UpdateCascade_tr@ID,其中Foo是表名
- 开始交易
- 第一次插入—从旧行中选择父表中具有新键或RID值的新行
- 其次,对于自上而下工作的所有子表,使用新键或RID值从旧行中插入并选择新行
- 第三,从下至上删除子表中具有旧键或RID值的行
- 最后,删除父表中具有旧键或RID值的行
- 提交事务
- 在更新所需行(父项加上所有子项)后禁用约束,然后启用约束,这不是一个人在在线生产环境中希望继续工作时会做的事情。该建议适用于单用户数据库
- 需要更改键或RID的值并不表示存在设计缺陷。这是一种普通的需要。通过选择稳定(非静态)的关键点可以缓解这种情况。它可以减轻,但不能消除
- 替代自然密钥的代理不会产生任何影响。在您给出的示例中,“key”是一个代理项。它需要更新
- 拜托,只是代理,没有“代理键”这样的东西,因为每个词都互相矛盾。要么是一个键(由数据组成),要么不是。代理项不是由数据组成的,它是显式的非数据。它没有键的任何属性
- 级联所有必需的更改并没有什么“棘手”的地方。请参阅上述步骤
- 没有什么可以阻止宇宙的变化。它改变了。处理它。由于数据库是关于宇宙的事实的集合,当宇宙发生变化时,数据库将不得不改变。这是大城市的生活,不适合新手
- 人们结婚和刺猬被埋都不是问题(尽管有这样的例子表明这是一个问题)。因为我们不使用名称作为键。我们使用小型、稳定的标识符,例如用于识别宇宙中的数据的标识符
- 名称、描述等在一行中存在一次。密钥存在于已迁移的任何位置。如果“键”是一个RID,那么RID也存在于它被迁移的任何地方
- 不要更新PK!这是我最近读到的第二个最搞笑的东西。添加一个新列是最重要的
- 如果您确信
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;