C'est tout l'intérêt des contraintes de clé étrangère: elles vous empêchent de supprimer des données qui sont référencées ailleurs afin de maintenir l'intégrité référentielle.
Il y a deux options:
- Supprimez d'
INVENTORY_ITEMS
abord les lignes , puis les lignes depuis STOCK_ARTICLES
.
- Utilisez
ON DELETE CASCADE
pour dans la définition de clé.
1: suppression dans le bon ordre
La manière la plus efficace de procéder varie en fonction de la complexité de la requête qui décide des lignes à supprimer. Un schéma général pourrait être:
BEGIN TRANSACTION
SET XACT_ABORT ON
DELETE INVENTORY_ITEMS WHERE STOCK_ARTICLE IN (<select statement that returns stock_article.id for the rows you are about to delete>)
DELETE STOCK_ARTICLES WHERE <the rest of your current delete statement>
COMMIT TRANSACTION
C'est très bien pour les requêtes simples ou pour supprimer un seul article en stock, mais étant donné que votre instruction delete contient une WHERE NOT EXISTS
clause imbriquée qui WHERE IN
pourrait produire un plan très inefficace, alors testez avec une taille de jeu de données réaliste et réorganisez la requête si nécessaire.
Notez également les instructions de transaction: vous voulez vous assurer que les deux suppressions sont terminées ou que ni l'une ni l'autre ne le font. Si l'opération se déroule déjà dans une transaction, vous devrez évidemment la modifier pour qu'elle corresponde à votre transaction actuelle et au processus de gestion des erreurs.
2: Utiliser ON DELETE CASCADE
Si vous ajoutez l'option en cascade à votre clé étrangère, SQL Server le fera automatiquement pour vous, supprimant les lignes de INVENTORY_ITEMS
pour satisfaire la contrainte selon laquelle rien ne doit faire référence aux lignes que vous supprimez. Ajoutez simplement ON DELETE CASCADE
à la définition FK comme ceci:
ALTER TABLE <child_table> WITH CHECK
ADD CONSTRAINT <fk_name> FOREIGN KEY(<column(s)>)
REFERENCES <parent_table> (<column(s)>)
ON DELETE CASCADE
Un avantage ici est que la suppression est une instruction atomique réduisant (bien que, comme d'habitude, ne supprimant pas à 100%) la nécessité de se soucier des paramètres de transaction et de verrouillage. La cascade peut même fonctionner sur plusieurs niveaux parent / enfant / petit-enfant / ... s'il n'y a qu'un seul chemin entre le parent et tous les descendants (recherchez "plusieurs chemins en cascade" pour des exemples de cas où cela pourrait ne pas fonctionner).
REMARQUE: moi-même et bien d'autres considérons que les suppressions en cascade sont dangereuses, donc si vous utilisez cette option, faites très attention à la documenter correctement dans la conception de votre base de données afin que vous et les autres développeurs ne survoliez pas le danger plus tard . J'évite les suppressions en cascade dans la mesure du possible pour cette raison.
Un problème courant causé par les suppressions en cascade est lorsque quelqu'un met à jour les données en supprimant et en recréant des lignes au lieu d'utiliser UPDATE
ou MERGE
. Ceci est souvent vu où "mettre à jour les lignes qui existent déjà, insérer celles qui n'en ont pas" (parfois appelé une opération UPSERT) est nécessaire et les personnes qui ne connaissent pas la MERGE
déclaration trouvent plus facile à faire:
DELETE <all rows that match IDs in the new data>
INSERT <all rows from the new data>
que
-- updates
UPDATE target
SET <col1> = source.<col1>
, <col2> = source.<col2>
...
, <colN> = source.<colN>
FROM <target_table> AS target JOIN <source_table_or_view_or_statement> AS source ON source.ID = target.ID
-- inserts
INSERT <target_table>
SELECT *
FROM <source_table_or_other> AS source
LEFT OUTER JOIN
<target_table> AS target
ON target.ID = source.ID
WHERE target.ID IS NULL
Le problème ici est que l'instruction delete se répercutera en cascade sur les lignes enfants et que l'instruction insert ne les recréera pas. Ainsi, lors de la mise à jour de la table parent, vous perdez accidentellement des données de la ou des tables enfant.
Sommaire
Oui, vous devez d'abord supprimer les lignes enfants.
Il y a une autre option: ON DELETE CASCADE
.
Mais ON DELETE CASCADE
peut être dangereux , alors utilisez-le avec soin.
Note latérale: utilisez MERGE
(ou UPDATE
-et- INSERT
où MERGE
n'est pas disponible) lorsque vous avez besoin d'une UPSERT
opération, pas DELETE
-puis-remplacez-le- INSERT
pour éviter de tomber dans des pièges posés par d'autres personnes utilisant ON DELETE CASCADE
.
INVENTORY_ITEMS
ajoutées entre les deuxDELETE
s.J'ai également rencontré ce problème et j'ai pu le résoudre. Voici ma situation:
Dans mon cas, j'ai une base de données utilisée pour signaler une analyse (MYTARGET_DB), qui tire d'un système source (MYSOURCE_DB). Certaines des tables «MYTARGET_DB» sont uniques à ce système, et les données y sont créées et gérées; La plupart des tableaux proviennent de «MYSOURCE_DB» et il existe un travail qui supprime / insère les données dans «MYTARGET_DB» de «MYSOURCE_DB».
L'une des tables de recherche [PRODUCT] provient de la SOURCE et une table de données [InventoryOutsourced] est stockée dans la TARGET. L'intégrité référentielle est conçue dans les tableaux. Ainsi, lorsque j'essaie d'exécuter la suppression / insertion, j'obtiens ce message.
La solution de contournement que j'ai créée consiste à insérer des données dans la variable de table [@tempTable] de [InventoryOutsourced], à supprimer des données dans [InventoryOutsourced], à exécuter les travaux de synchronisation, à insérer dans [InventoryOutsourced] à partir de [@tempTable]. Cela maintient l'intégrité en place et la collecte de données unique est également conservée. Quel est le meilleur des deux mondes. J'espère que cela t'aides.
la source
Je n'ai pas entièrement testé, mais quelque chose comme ça devrait fonctionner.
la source