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_ITEMSprimero, luego las filas de STOCK_ARTICLES.
- Use
ON DELETE CASCADEpara 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 EXISTScláusula de anidamiento que WHERE INpodrí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_ITEMSpara satisfacer la restricción de que nada debe referirse a las filas que está eliminando. Simplemente agregue ON DELETE CASCADEa 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 UPDATEo 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 MERGEdeclaració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 CASCADEpuede ser peligroso , así que úselo con cuidado.
Nota al margen: use MERGE(o UPDATE-y- INSERTdonde MERGEno esté disponible) cuando necesite una UPSERToperación, no DELETE -y luego reemplace- INSERTpara evitar caer en trampas colocadas por otras personas ON DELETE CASCADE.
INVENTORY_ITEMSagregados entre los dosDELETEs.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