Pregunta

Aquí hay un ejemplo de lo que estoy pasando:

CREATE TABLE Parent (id BIGINT NOT NULL,
  PRIMARY KEY (id)) ENGINE=InnoDB;

CREATE TABLE Child (id BIGINT NOT NULL,
  parentid BIGINT NOT NULL,
  PRIMARY KEY (id),
  KEY (parentid),
  CONSTRAINT fk_parent FOREIGN KEY (parentid) REFERENCES Parent (id) ON DELETE CASCADE) ENGINE=InnoDB;

CREATE TABLE Uncle (id BIGINT NOT NULL,
  parentid BIGINT NOT NULL,
  childid BIGINT NOT NULL,
  PRIMARY KEY (id),
  KEY (parentid),
  KEY (childid),
  CONSTRAINT fk_parent_u FOREIGN KEY (parentid) REFERENCES Parent (id) ON DELETE CASCADE,
  CONSTRAINT fk_child FOREIGN KEY (childid) REFERENCES Child (id)) ENGINE=InnoDB;

Observe que no hay ON DELETE CASCADE para la relación tío-hijo;es decir.Eliminar a un Niño no elimina a sus Tíos y viceversa.

Cuando tengo un padre y un tío con el mismo hijo y elimino al padre, parece como InnoDB debería poder simplemente "resolverlo" y dejar que la cascada se propague por toda la familia (es decir,eliminar al Padre también elimina al Tío y al Niño).Sin embargo, en lugar de eso, aparece lo siguiente:

  ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`cascade_test/uncle`, CONSTRAINT `fk_child` FOREIGN KEY (`childid`) REFERENCES `child` (`id`))

InnoDB está intentando eliminar en cascada al niño antes que los tíos que se refieren a él.

¿Me estoy perdiendo de algo?Es esto supuesto fallar por alguna razón que no entiendo?¿O hay algún truco para que funcione (o es un error en MySQL)?

¿Fue útil?

Solución

La eliminación del padre está provocando la eliminación del hijo como dijiste y no sé por qué va a la mesa del hijo antes que a la mesa del tío.Me imagino que tendrías que mirar el código dbms para estar seguro, pero estoy seguro de que hay un algoritmo que elige a qué tablas conectar en cascada primero.

El sistema realmente no "descubre" las cosas de la manera que usted implica aquí y simplemente sigue sus reglas de restricción.El problema es que el esquema que creó encuentra una restricción que no le permitirá seguir adelante.

Veo lo que dices..si llegara primero a la mesa del tío, eliminaría el registro y luego eliminaría al niño (y no llegaría a la cascada del tío desde la eliminación del niño).Pero aun así, no creo que se establezca un esquema que se base en ese tipo de comportamiento en la realidad.Creo que la única manera de saber con seguridad qué está pasando es revisar el código o hacer que uno de los programadores de mysql/postgresql esté aquí para decir cómo procesa las restricciones fk.

Otros consejos

En el caso más simple, ¿qué sucede si se elimina un registro de Niño y tiene un Tío de referencia?Eso no está especificado, por lo que las restricciones fallan de todos modos.

Si eliminar a un niño no elimina a sus tíos, ¿qué sucede en su lugar?Uncle.childid no puede ser nulo.

Lo que quieres es una de estas tres cosas:

  1. Uncle.childid puede ser nulo y desea ON DELETE SET NULL para childid.
  2. Uncle.childid no puede ser nulo y desea ON DELETE CASCADE para childid.
  3. Childid no pertenece a Uncle y desea una relación ChildsUncle con restricciones de clave externa ON DELETE CASCADE tanto para Child como para Uncle.Uncleid sería una clave candidata para esa relación (es decir,debe ser único).

@Matt Solnit, en primer lugar, esta es realmente una buena pregunta y, hasta donde yo sé, cuando se debe eliminar un registro de Parent, innodb primero intenta identificar qué otras tablas contienen referencias a él para poder eliminar el registro de ellas como Bueno.En su caso, es la tabla Child y la tabla Uncle, ahora parece que en este caso decide eliminar primero el registro de la tabla Child y, por lo tanto, repite el mismo proceso para Child y finalmente falla ya que el Uncle tiene una referencia a la tabla Child pero ni "ON". DELETE CASCADE" ni "ON DELETE SET NULL" se especifican para fk_child FK en la tabla Uncle.Sin embargo, parece que si innodb primero intenta eliminar el registro de la tabla Uncle y luego la eliminación debería haberse realizado sin problemas.Bueno, después de pensarlo dos veces, creo que dado que innodb sigue el modelo ACID, elige Child en lugar de Uncle para iniciar el proceso de eliminación porque si comienza con Uncle, incluso entonces la eliminación en Child aún podría haber fallado, por ejemplo.supongamos una tabla de amigos que tiene la clave fk_child (similar a la del tío) sin ON DELETE CASCADE, ahora esto aún habría causado que toda la transacción fallara y, por lo tanto, este comportamiento me parece correcto.En otras palabras, innodb comienza con una tabla que puede causar un posible error en la transacción, pero esa es mi teoría, en realidad podría ser una historia completamente diferente.:)

el diseño está todo mal.Debería tener una sola tabla, con relación padre-hijo (literalmente).Entonces puedes descubrir tíos (y tías) con una consulta.

select id from persons where -find all children of the grandparents
parent id in (
select parentid from persons --find the grandparents
where id in (
select parentid from persons --find the parents
where id=THECHILD) )
minus --and take out the child's parents
select parentid from persons
where id=THECHILD

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top