Question

Voici un exemple de ce que j'ai en train de faire :

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;

Notez qu'il n'y a pas de CASCADE ON DELETE pour la relation Oncle-Enfant ;c'est à dire.supprimer un Enfant ne supprime pas son(ses) Oncle(s) et vice-versa.

Lorsque j'ai un parent et un oncle avec le même enfant et que je supprime le parent, cela semble comme InnoDB devrait être capable de simplement « comprendre » et laisser la cascade se répercuter sur toute la famille (c'est-à-direla suppression du parent supprime également l'oncle et l'enfant).Cependant, à la place, j'obtiens ce qui suit :

  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 essaie de supprimer en cascade l'enfant avant le ou les oncles qui y font référence.

Est-ce que j'ai raté quelque chose ?Est-ce censé échouer pour une raison que je ne comprends pas ?Ou existe-t-il une astuce pour le faire fonctionner (ou est-ce un bug dans MySQL) ?

Était-ce utile?

La solution

La suppression du parent déclenche la suppression de l'enfant comme vous l'avez dit et je ne sais pas pourquoi il va à la table des enfants avant la table de l'oncle.J'imagine que vous devrez consulter le code dbms pour en être sûr, mais je suis sûr qu'il existe un algorithme qui sélectionne les tables vers lesquelles cascader en premier.

Le système ne « comprend » pas vraiment les choses comme vous le sous-entendez ici et il suit simplement ses règles de contraintes.Le problème est le schéma que vous avez créé dans la mesure où il rencontre une contrainte qui ne le laissera pas aller plus loin.

J'entends ce que vous dites..s'il frappait d'abord la table oncle, il supprimerait l'enregistrement, puis supprimerait l'enfant (et ne frapperait pas la cascade oncle de la suppression de l'enfant).Mais même ainsi, je ne pense pas qu’un schéma serait mis en place pour s’appuyer sur ce genre de comportement dans la réalité.Je pense que la seule façon de savoir avec certitude ce qui se passe est de parcourir le code ou de demander à l'un des programmeurs mysql/postgresql ici de dire comment il traite les contraintes fk.

Autres conseils

Dans le cas le plus simple, que se passe-t-il si un enregistrement est supprimé de Child et qu'il fait référence à un oncle ?Ce n'est pas spécifié, donc les contraintes échouent de toute façon.

Si la suppression d’un enfant ne supprime pas ses oncles, que se passe-t-il à la place ?Uncle.childid ne peut pas être nul.

Ce que vous voulez, c'est l'une de ces trois choses :

  1. Uncle.childid peut être nul et vous souhaitez ON DELETE SET NULL pour childid.
  2. Uncle.childid ne peut pas être nul et vous souhaitez ON DELETE CASCADE pour childid.
  3. Childid n'appartient pas à Oncle et vous souhaitez une relation ChildsUncle avec des contraintes de clé étrangère ON DELETE CASCADE pour Child et Oncle.Oncleid serait une clé candidate pour cette relation (c'est-à-direil devrait être unique).

@Matt Solnit tout d'abord, c'est vraiment une bonne question et pour autant que je sache, lorsqu'un enregistrement du parent doit être supprimé, innodb essaie d'abord d'identifier quelles autres tables contiennent des références à celui-ci afin de pouvoir en supprimer l'enregistrement. Bien.Dans votre cas, il s'agit de la table Enfant et de la table Oncle. Il semble maintenant que dans ce cas, il décide de supprimer d'abord l'enregistrement de la table Enfant et répète donc le même processus pour l'enfant et échoue finalement car l'oncle détient une référence à la table Enfant mais ni "ON". DELETE CASCADE" ni "ON DELETE SET NULL" ne sont spécifiés pour fk_child FK dans la table Uncle.Il semble cependant que si innodb essaie d'abord de supprimer l'enregistrement de la table Oncle, puis la suppression aurait dû se dérouler sans problème.Eh bien, après y avoir réfléchi, je pense que puisque innodb suit le modèle ACID, il choisit donc Child plutôt que Oncle pour démarrer le processus de suppression, car s'il commence par Oncle, même alors, la suppression dans Child pourrait toujours avoir échoué, par exemple.supposons une table Friend qui a une clé fk_child (similaire à Uncle) sans ON DELETE CASCADE, maintenant cela aurait quand même provoqué l'échec de toute la transaction et donc cela me semble un bon comportement.En d'autres termes, innodb commence par une table qui peut entraîner un éventuel échec de la transaction, mais c'est ma théorie. En réalité, cela pourrait être une histoire complètement différente.:)

la conception est complètement fausse.Vous devriez avoir une seule table, avec une relation parent-enfant (littéralement).Ensuite, vous pouvez déterminer les oncles (et tantes) avec une requête

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

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top