2009-10-28 9 views
15

En MSSQL 2005 yo sólo golpeó el mensaje de error infame:¿Cuál es el problema con la clave externa en cascada de múltiples rutas y ciclos?

Presentación de restricción FOREIGN KEY XXX YYY mesa puede causar ciclos o múltiples rutas en cascada. Especifique ON DELETE NO ACTION o ON UPDATE NO ACTION, o modifique otras restricciones FOREIGN KEY.

Ahora, StackOverflow tiene varios temas sobre este mensaje de error, así que ya tengo la solución (en mi caso tendré que usar activadores), pero tengo curiosidad de por qué hay tal problema en absoluto.

Según tengo entendido, hay básicamente dos escenarios que quieren evitar: un ciclo y varias rutas. Un ciclo sería cuando dos tablas tienen cascadas claves foráneas entre sí. De acuerdo, un ciclo puede abarcar varias tablas también, pero este es el caso básico y será más fácil de analizar.

Las rutas múltiples serían cuando TableA tiene claves externas a TableB y TableC, y TableB también tiene una clave externa a TableC. Nuevamente, este es el caso básico mínimo.

No puedo ver ningún problema que pueda surgir cuando se elimine o actualice un registro en cualquiera de esas tablas. Claro, es posible que deba consultar la misma tabla varias veces para ver qué registros necesitan actualización/eliminación, pero ¿es realmente un problema? ¿Es esto un problema de rendimiento?

En otros temas de SO las personas llegan a etiquetar usando cascadas como "risky" y afirman que "resolving cascade paths is a complex problem". ¿Por qué? ¿Dónde está el riesgo? ¿Dónde está el problema?

Respuesta

4

Tiene una tabla secundaria con 2 rutas en cascada del mismo elemento principal: una "eliminar", una "nula".

¿Qué tiene prioridad? ¿Qué esperas después? etc.

Nota: Un activador es un código y puede agregar cierta inteligencia o condiciones a una cascada.

0

Estoy de acuerdo con que las cascadas son "riesgosas" y deben evitarse. (Personalmente prefiero poner en cascada los cambios de forma manual en lugar de tener el servidor sql que se ocupa automáticamente de ellos). Esto es también porque, incluso si el servidor SQL elimina millones de filas, la salida todavía aparecería como

(1 row (s) affected)

+1

¿Reemplazarlo con un gatillo lo haría mejor? Por supuesto, existe un enfoque como "no usar claves foráneas en absoluto" y hacer todo desde el código. No creo que deba decirte por qué creo que es una mala idea ... –

1

La razón por la que no lo quiera usar eliminación en cascada tiene que ver con el rendimiento y bloqueo . Sí, no es tan malo cuando elimina un registro, pero tarde o temprano tendrá que eliminar un gran grupo de registros y su base de datos se detendrá.

Si está eliminando suficientes registros, SQL Server puede escalar a un bloqueo de tabla y nadie puede hacer nada con la tabla hasta que finalice.

Recientemente trasladamos uno de nuestros clientes a su propio servidor. Como parte del acuerdo, también tuvimos que eliminar todos los registros de ese cliente de nuestro servidor original. Eliminar toda su información en lotes (para no causar problemas con otros usuarios) tomó un par de meses. Si tuviéramos la configuración de eliminación en cascada, la base de datos habría sido inaccesible para los otros clientes durante mucho tiempo, ya que millones de registros se eliminaron en una transacción y cientos de tablas se bloquearon hasta que se realizó la transacción.

También pude ver un escenario donde podría haber un punto muerto al usar la eliminación en cascada porque no tenemos control sobre el orden que habría tomado la ruta en cascada y nuestra base de datos está un tanto desnormalizada con clientid apareciendo en la mayoría de las tablas. Entonces, si bloqueaba la una tabla que tenía una clave externa también a una tercera tabla, así como a la tabla cliente que estaba en una ruta diferente, posiblemente no podría verificar esa tabla para eliminarla de la tercera tabla porque esto es todo una transacción y los bloqueos no se liberarán hasta que se haya completado. Por lo tanto, posiblemente no nos hubiera permitido configurar eliminaciones en cascada si vio la posibilidad de crear bloqueos en la transacción.

Otra razón para evitar eliminaciones en cascada es que a veces la existencia de un registro secundario es motivo suficiente para no eliminar el registro principal. Por ejemplo, si tiene una tabla de clientes y ese cliente ha tenido pedidos en el pasado, no le gustaría eliminarlo y perder la información sobre el pedido real.

+4

Si trabajas con millones de filas, casi siempre tienes que usar soluciones complicadas como el procesamiento por lotes, deshabilitar las claves externas, ejecutar el trabajo en tiempos de inactividad, etc. Pero eso no es un escenario cotidiano. La gente se prepara para ese tipo de cosas de antemano. Lo prueban en un entorno de prueba. Hacen scripts largos y retorcidos que modifican la base de datos para que tenga un rendimiento aceptable. Y una vez que está hecho, se olvida. Pero "ON DELETE CASCADE" está destinado a ser utilizado en la vida cotidiana, cuando no está eliminando millones de filas, sino a lo sumo unos pocos cientos a la vez. –

+0

Y los interbloqueos en cascadas (o incluso eliminaciones simples para varias filas en la misma tabla) pueden ocurrir de todos modos, prevenir múltiples caminos y ciclos de cascada no hace un poco para mejorarlo. –

1

considere una tabla de empleados:

CREATE TABLE Employee 
(
    EmpID INTEGER NOT NULL PRIMARY KEY, 
    Name VARCHAR(40) NOT NULL, 
    MgrID INTEGER NOT NULL REFERENCES Employee(EmpID) ON DELETE CASCADE 
); 

INSERT INTO Employees( 1, "Bill", 1); 
INSERT INTO Employees( 23, "Steve", 1); 
INSERT INTO Employees(234212, "Helen", 23); 

Supongamos ahora que Bill se retira:

DELETE FROM Employees WHERE Name = "Bill"; 

Ooooppps; ¡Todos fueron despedidos!

[Podemos debatir si los detalles de la sintaxis son correctos; el concepto está parado, creo.]

+13

Argumentaría que en este caso el CASO DE DELETE ON DELETE se usó incorrectamente. Lo sentimos, es tu culpa si creas un programa con errores: no es el trabajo del lenguaje de programación el protegerlo de escribir código incorrecto. –

-1

Creo que si usar una opción ON DELETE CASCADE es una cuestión del modelo de negocio que está implementando. Una relación entre dos objetos comerciales podría ser una simple "asociación", donde ambos extremos de la relación están relacionados, pero por lo demás son objetos independientes cuyo ciclo de vida es diferente y está controlado por otra lógica. Sin embargo, también existen relaciones de "agregación", donde un objeto podría ser visto como el "padre" o el "dueño" de un objeto "niño" o "detalle". Existe la noción aún más fuerte de una relación de "composición", donde un objeto existe únicamente como una composición de varias partes. En el caso de "asociación", generalmente no declarará una restricción ON DELETE CASCADE. Para agregaciones o composiciones, sin embargo, ON DELETE CASCADE le ayuda a mapear su modelo de negocio a la base de datos con mayor precisión y de manera declarativa. Es por eso que me molesta que MS SQL Server restrinja el uso de esta opción a una sola ruta en cascada. Si no me equivoco, muchos otros sistemas de bases de datos SQL ampliamente utilizados no imponen tales restricciones.

+0

Y esta es una respuesta ... ¿cómo? –

1

Creo que el problema es que cuando se hace una ruta "ON DELETE CASCADE" y la otra "ON DELETE RESTRICT", o "NO ACTION" el resultado (el resultado) es impredecible. Depende de qué desencadenador de eliminación (esto es también un desencadenante, pero uno que no tiene que crear usted mismo) se ejecutará primero.

+0

Con las transacciones, esto no debería ser un problema: se borra la fila, se eliminan las cascadas, luego se restringe la eliminación y todo se retrotrae. – Brilliand

Cuestiones relacionadas