Voici mon problème: J'ai 2 tables:
|ID|OTHER_STAF|
, où ID est la clé primaire|FPK|ID|SOMETHING_ELSE|
, où la combinaison FPK et ID constituent la clé primaire et ID est également une clé étrangère référencée à WORKER.ID (non null et doit avoir la même valeur que dans WORKER).Je souhaite créer la procédure stockée UPDATE_ID_WORKER, dans laquelle je souhaite modifier la valeur de l'ID spécifique dans WORKER, ainsi que dans toutes les instances de la valeur spécifique de l'ID dans FIRM.
procédure stockée:
........ @ id .. ???? ........
Ne faites pas cela, mais insérez plutôt un nouvel enregistrement et mettez-le à jour de cette façon.
Mais si vous en avez vraiment besoin, vous pouvez procéder comme suit:
ALTER TABLE foo WITH NOCHECK CONSTRAINT ALL
)Tout d'abord, nous choisissons des colonnes de données stables (non statiques) pour former une clé primaire, précisément parce que nous souhaitons éviter de mettre à jour des clés dans une base de données relationnelle (dans laquelle les références sont classées par clé).
Pour ce problème, peu importe que la clé soit une clé relationnelle ("constituée des données") et qu’elle possède donc l’intégrité relationnelle, la puissance et la vitesse, ou si la "clé" est un ID d’enregistrement, avec aucun de cette intégrité relationnelle, puissance et vitesse. L'effet est le même.
J'affirme cela, car de nombreux messages rédigés par des personnes désemparées suggèrent que c'est la raison exacte pour laquelle les ID d'enregistrement sont en quelque sorte supérieurs aux clés relationnelles.
Le fait est que l'ID de clé ou d'enregistrement est migré partout où une référence est requise.
Deuxièmement, si vous devez modifier la valeur de la clé ou de l’ID d’enregistrement, vous devez la modifier. Voici la méthode OLTP conforme à la norme. Notez que les fournisseurs haut de gamme n'autorisent pas la "mise à jour en cascade".
Ecrire un proc. Foo_UpdateCascade_tr @ID, où Foo est le nom de la table
Commencer une transaction
D'abord INSERT-SELECT une nouvelle ligne dans la table parente, à partir de l'ancienne ligne, avec la nouvelle valeur Key ou RID
Deuxièmement, pour toutes les tables enfants, en commençant par le haut, INSERT-SELECT les nouvelles lignes, à partir des anciennes lignes, avec la nouvelle valeur Key ou RID
Troisièmement, SUPPRIMEZ les lignes dans les tables enfants qui ont l'ancienne valeur de clé ou RID, de bas en haut.
Enfin, supprimez la ligne de la table parente qui contient l'ancienne valeur de clé ou RID
Commettre la transaction
Les autres réponses sont incorrectes.
Désactiver les contraintes, puis les activer, après avoir mis à jour les lignes requises (parent plus tous les enfants) n’est pas ce qu’une personne ferait dans un environnement de production en ligne si elle souhaite rester. Ce conseil est bon pour les bases de données mono-utilisateur.
La nécessité de modifier la valeur d'une clé ou d'un RID n'indique pas un défaut de conception. C'est un besoin ordinaire. Cela est atténué en choisissant des clés stables (non statiques). Cela peut être atténué, mais on ne peut pas l'éliminer.
Un substitut substituant une clé naturelle ne fera aucune différence. Dans l'exemple que vous avez donné, la "clé" est un substitut. Et il doit être mis à jour.
Il n'y a rien de "délicat" à appliquer en cascade toutes les modifications requises. Reportez-vous aux étapes indiquées ci-dessus.
Rien ne peut être empêché si l’univers est en train de changer. Ça change. Faites avec. Et comme la base de données est un ensemble de faits sur l’univers, lorsque celui-ci changera, la base de données devra changer. C'est la vie dans la grande ville, ce n'est pas pour les nouveaux joueurs.
Les gens qui se marient et les hérissons qui se font enterrer ne sont pas un problème (malgré de tels exemples suggérant que cela est un problème). Parce que nous n'utilisons pas les noms comme clés. Nous utilisons de petits identificateurs stables, tels que ceux utilisés pour identifier les données de l'univers.
Ne mettez pas à jour le PK! est la deuxième chose la plus hilarante que j'ai lue depuis un moment. Ajouter une nouvelle colonne est le plus.
Si vous êtes sûr que cette modification convient à l'environnement dans lequel vous travaillez: définissez les conditions FK des tables secondaires sur UPDATE CASCADING.
Par exemple, si vous utilisez SSMS comme interface graphique:
Lorsque vous mettez ensuite à jour une valeur dans la colonne PK de votre table primaire, les références FK des autres tables sont mises à jour pour pointer sur la nouvelle valeur, en préservant l'intégrité des données.
Lorsque vous estimez qu'il est nécessaire de mettre à jour une valeur de clé primaire ainsi que toutes les clés étrangères correspondantes, l'ensemble de la conception doit être corrigé.
Il est difficile de mettre en cascade tous les changements de clés étrangères nécessaires. Il est recommandé de ne jamais mettre à jour la clé primaire. Si vous le jugez nécessaire, vous devez utiliser un Surrogate Primary Key
, clé qui n'est pas dérivée des données de l'application. Par conséquent, sa valeur n’est pas liée à la logique d’entreprise et n’a jamais besoin d’être modifiée (et doit être invisible pour l’utilisateur final). Vous pouvez ensuite mettre à jour et afficher une autre colonne.
par exemple:
BadUserTable
UserID varchar(20) primary key --user last name
other columns...
lorsque vous créez de nombreuses tables ayant un FK en UserID, afin de suivre tout ce sur quoi l'utilisateur a travaillé, mais que cet utilisateur se marie ensuite et souhaite un ID correspondant à son nouveau nom de famille, vous n'avez aucune chance.
GoodUserTable
UserID int identity(1,1) primary key
UserLogin varchar(20)
other columns....
fK maintenant la clé primaire de substitution pour toutes les autres tables, et affiche UserLogin lorsque cela est nécessaire, leur permet de se connecter en utilisant cette valeur, et quand ils doivent la modifier, vous le modifiez dans une colonne de une seule ligne.
Ne met pas à jour la clé primaire . Cela pourrait vous causer beaucoup de problèmes si vous gardez vos données intactes, si vous avez d'autres tables qui les référencent.
Idéalement, si vous voulez un champ unique pouvant être mis à jour, créez un nouveau champ.
Vous pouvez utiliser cette fonction récursive pour générer le script T-SQL nécessaire.
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;