Pergunta

Aqui está um exemplo do que estou acontecendo:

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 não há ON DELETE CASCADE para o relacionamento Tio-Filho;ou sejaexcluir um filho não exclui seu(s) tio(s) e vice-versa.

Quando tenho um Pai e um Tio com o mesmo Filho e excluo o Pai, ele parece como o InnoDB deve ser capaz de "descobrir" e deixar a cascata se espalhar por toda a família (ou seja,excluir o pai também exclui o tio e a criança).No entanto, em vez disso, recebo o seguinte:

  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`))

O InnoDB está tentando excluir em cascata o filho antes do(s) tio(s) que se refere(m) a ele.

Estou esquecendo de algo?É isto suposto falhar por algum motivo que não entendo?Ou existe algum truque para fazê-lo funcionar (ou é um bug no MySQL)?

Foi útil?

Solução

A exclusão do pai está acionando a exclusão do filho, como você afirmou, e não sei por que ela vai para a tabela filho antes da tabela do tio.Eu imagino que você teria que olhar o código dbms para ter certeza, mas tenho certeza de que existe um algoritmo que escolhe quais tabelas serão conectadas em cascata primeiro.

O sistema realmente não 'descobre' as coisas da maneira que você insinua aqui e está apenas seguindo suas regras de restrição.O problema é o esquema que você criou, pois ele encontra uma restrição que não o deixa passar mais.

Eu vejo o que você está dizendo..se atingisse a tabela do tio primeiro, excluiria o registro e depois excluiria o filho (e não atingiria a cascata do tio a partir da exclusão do filho).Mas, mesmo assim, não creio que fosse criado um esquema para se basear nesse tipo de comportamento na realidade.Eu acho que a única maneira de saber com certeza o que está acontecendo é examinar o código ou pedir a um dos programadores mysql/postgresql aqui para dizer como ele processa as restrições fk.

Outras dicas

No caso mais simples, o que acontece se um registro for excluído de Filho e tiver uma referência ao Tio?Isso não é especificado, então as restrições falham de qualquer maneira.

Se excluir uma criança não exclui seus tios, o que acontece?Tio.childid não pode ser nulo.

O que você quer é uma destas três coisas:

  1. Uncle.childid pode ser nulo e você deseja ON DELETE SET NULL para childid.
  2. Uncle.childid não pode ser nulo e você deseja ON DELETE CASCADE para childid.
  3. Childid não pertence ao Uncle e você deseja uma relação ChildsUncle com restrições de chave estrangeira ON DELETE CASCADE para Child e Uncle.Uncleid seria uma chave candidata para essa relação (ou seja,deve ser único).

@Matt Solnit, em primeiro lugar, esta é realmente uma boa pergunta e, até onde eu sei, quando um registro de Parent deve ser excluído, o innodb primeiro tenta identificar quais outras tabelas contêm referências a ele para que possa excluir o registro delas como bem.No seu caso é a tabela Filho e a tabela Tio, agora parece que neste caso ele decide primeiro deletar o registro da tabela Filho e assim repete o mesmo processo para Filho e eventualmente falha porque o Tio mantém referência à tabela Filho mas nem "ON DELETE CASCADE" nem "ON DELETE SET NULL" são especificados para fk_child FK na tabela Uncle.No entanto, parece que se O innodb primeiro tenta excluir o registro da tabela Uncle, então a exclusão deve ter ocorrido sem problemas.Bem, depois de pensar duas vezes, acho que, como o innodb segue o modelo ACID, ele escolhe Filho em vez de Tio para iniciar o processo de exclusão, porque se começar com Tio, mesmo assim, a exclusão em Filho ainda pode ter falhado, por exemplo.suponha uma tabela Friend que tenha a chave fk_child (semelhante ao Uncle) sem ON DELETE CASCADE, agora isso ainda teria causado a falha de toda a transação e, portanto, parece um comportamento correto para mim.Em outras palavras, o innodb começa com uma tabela que pode causar uma possível falha na transação, mas essa é a minha teoria, na realidade pode ser uma história completamente diferente.:)

o design está todo errado.Você deve ter uma tabela única, com relacionamento pai-filho (literalmente).Então você pode descobrir tios (e tias) com uma 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 em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top