Ese es el objetivo de las restricciones de clave externa: le impiden eliminar datos a los que se hace referencia en otros lugares para mantener la integridad referencial.
Hay dos opciones:
- Eliminar las filas de
INVENTORY_ITEMS
primero, luego las filas de STOCK_ARTICLES
.
- Use
ON DELETE CASCADE
para el en la definición clave.
1: Eliminar en el orden correcto
La forma más eficiente de hacerlo varía según la complejidad de la consulta que decide qué filas eliminar. Un patrón general podría ser:
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
Esto está bien para consultas simples o para eliminar un solo artículo de stock, pero dado que su declaración de eliminación contiene una WHERE NOT EXISTS
cláusula de anidamiento que WHERE IN
podría producir un plan muy ineficiente, pruebe con un tamaño de conjunto de datos realista y reorganice la consulta si es necesario.
También tenga en cuenta las declaraciones de transacción: desea asegurarse de que ambas eliminaciones se completen o que ninguna de ellas lo haga. Si la operación ya está ocurriendo dentro de una transacción, obviamente necesitará modificar esto para que coincida con su transacción actual y el proceso de manejo de errores.
2: uso ON DELETE CASCADE
Si agrega la opción en cascada a su clave externa, SQL Server lo hará automáticamente por usted, eliminando filas INVENTORY_ITEMS
para satisfacer la restricción de que nada debe referirse a las filas que está eliminando. Simplemente agregue ON DELETE CASCADE
a la definición FK de esta manera:
ALTER TABLE <child_table> WITH CHECK
ADD CONSTRAINT <fk_name> FOREIGN KEY(<column(s)>)
REFERENCES <parent_table> (<column(s)>)
ON DELETE CASCADE
Una ventaja aquí es que la eliminación es una declaración atómica que reduce (aunque, como de costumbre, no elimina al 100%) la necesidad de preocuparse por la configuración de la transacción y el bloqueo. La cascada incluso puede operar en varios niveles padre / hijo / nieto / ... si solo hay una ruta entre el padre y todos los descendientes (busque "rutas en cascada múltiples" para ver ejemplos de dónde esto podría no funcionar).
NOTA: Yo, y muchos otros, consideramos que las eliminaciones en cascada son peligrosas, por lo tanto, si usa esta opción, tenga mucho cuidado de documentarla adecuadamente en el diseño de su base de datos para que usted y otros desarrolladores no tropiecen con el peligro más adelante . Evito las eliminaciones en cascada siempre que sea posible por este motivo.
Un problema común causado con las eliminaciones en cascada es cuando alguien actualiza los datos al soltar y volver a crear filas en lugar de usar UPDATE
o MERGE
. Esto se ve a menudo cuando se necesita "actualizar las filas que ya existen, insertar las que no" (a veces llamada operación UPSERT) y las personas que desconocen la MERGE
declaración encuentran más fácil hacerlo:
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
El problema aquí es que la declaración de eliminación se conectará en cascada a las filas secundarias, y la instrucción de inserción no las volverá a crear, por lo que al actualizar la tabla primaria, accidentalmente pierde datos de la (s) tabla (s) secundaria (s).
Resumen
Sí, primero debe eliminar las filas secundarias.
Hay otra opción: ON DELETE CASCADE
.
Pero ON DELETE CASCADE
puede ser peligroso , así que úselo con cuidado.
Nota al margen: use MERGE
(o UPDATE
-y- INSERT
donde MERGE
no esté disponible) cuando necesite una UPSERT
operación, no DELETE
-y luego reemplace- INSERT
para evitar caer en trampas colocadas por otras personas ON DELETE CASCADE
.
INVENTORY_ITEMS
agregados entre los dosDELETE
s.También me encontré con este problema y pude resolverlo. Aquí está mi situación:
En mi caso, tengo una base de datos utilizada para informar un análisis (MYTARGET_DB), que se extrae de un sistema fuente (MYSOURCE_DB). Algunas de las tablas 'MYTARGET_DB' son exclusivas de ese sistema, y los datos se crean y administran allí; La mayoría de las tablas son de 'MYSOURCE_DB' y hay un trabajo que elimina / inserta los datos en 'MYTARGET_DB' de 'MYSOURCE_DB'.
Una de las tablas de búsqueda [PRODUCT] es de SOURCE, y hay una tabla de datos [InventoryOutsourced] almacenada en TARGET. Hay integridad referencial diseñada en las tablas. Entonces, cuando intento ejecutar la eliminación / inserción, aparece este mensaje.
La solución alternativa que creé es insertar datos en la variable de tabla [@tempTable] de [InventoryOutsourced], eliminar datos en [InventoryOutsourced], ejecutar los trabajos de sincronización, insertarlos en [InventoryOutsourced] de [@tempTable]. Esto mantiene la integridad en su lugar, y la recopilación de datos única también se conserva. Que es lo mejor de ambos mundos. Espero que esto ayude.
fuente
No lo he probado completamente, pero algo como esto debería funcionar.
fuente