12

これが私が行っていることの例です:

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;

Uncle-Child関係にはONDELETECASCADEがないことに注意してください。つまり、子を削除してもその叔父は削除されません。その逆も同様です。

親と同じ子を持つ叔父がいて、親を削除すると、InnoDBは「それを理解」して、家族全体にカスケードを波及させることができるはずです(つまり、親を削除すると叔父が削除されます)と子供も)。ただし、代わりに、次のようになります。

  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は、子を参照する叔父の前に子をカスケード削除しようとしています。

私は何かが足りないのですか?どういうわけかわからないので失敗するのでしょう?それとも、それを機能させるためのトリックがありますか(またはMySQLのバグですか)?

4

4 に答える 4

8

より単純なケースでは、レコードが子から削除され、参照している叔父がいる場合はどうなりますか?それは指定されていないので、とにかく制約は失敗します。

子を削除してもその叔父が削除されない場合、代わりに何が起こりますか?Uncle.childidをnullにすることはできません。

必要なのは、次の3つのうちの1つです。

  1. Uncle.childidはnullにすることができ、childidにはON DELETESETNULLが必要です。
  2. Uncle.childidをnullにすることはできません。また、childidにONDELETECASCADEが必要です。
  3. ChildidはUncleに属していないため、ChildとUncleの両方に対するONDELETECASCADE外部キー制約を持つChildsUncle関係が必要です。Uncleidは、その関係の候補キーになります(つまり、一意である必要があります)。
于 2008-09-13T00:08:22.443 に答える
3

あなたが述べたように、親の削除が子の削除を引き起こしていますが、叔父テーブルの前に子テーブルに移動する理由がわかりません。確かに知るにはdbmsコードを見る必要があると思いますが、最初にカスケードするテーブルを選択するアルゴリズムがあると確信しています。

システムは、ここで暗示する方法で実際に「把握」するのではなく、その制約ルールに従っているだけです。問題は、それ以上通過させない制約に遭遇したという点で、作成したスキーマです。

私はあなたが言っていることを理解しています..最初に叔父のテーブルにヒットすると、レコードが削除され、次に子が削除されます(子の削除から叔父のカスケードにはヒットしません)。しかし、そうであっても、実際にはそのような動作に依存するようにスキーマが設定されるとは思いません。何が起こっているのかを確実に知る唯一の方法は、コードを調べるか、ここにいる mysql/postgresql プログラマーの 1 人に fk 制約の処理方法を説明してもらうことだと思います。

于 2008-09-16T22:38:24.373 に答える
0

@Matt Solnitまず第一に、これは本当に良い質問です。私が知る限り、親からのレコードがいつ削除されるかを知る限り、innodbは最初に他のテーブルへの参照を保持している他のテーブルを識別して、それらからレコードを削除できるようにします良い。あなたの場合は子テーブルと叔父テーブルですが、この場合、最初に子テーブルからレコードを削除することを決定したようです。したがって、子に対して同じプロセスを繰り返し、最終的には叔父が子テーブルへの参照を保持しますが、どちらも "ONアンクルテーブルのfk_child FKに「DELETE CASCADE」または「ON DELETE SET NULL」が指定されています。ただしinnodb は最初に Uncle テーブルからレコードを削除しようとしますが、削除はスムーズに行われるはずです。考え直した後、innodb は ACID モデルに従っているため、削除プロセスを開始するために Uncle ではなく Child を選択すると思います。 (叔父さんに似ています) ON DELETE CASCADE がなくても、これでもトランザクション全体が失敗する可能性があるため、これは正しい動作に見えます。言い換えれば、innodbはトランザクションで失敗する可能性のあるテーブルから始まりますが、それは実際には私の理論であり、まったく別の話かもしれません. :)

于 2013-05-07T17:51:47.487 に答える
-3

設計がすべて間違っています。親子関係(文字通り)を持つ単一のテーブルが必要です。次に、クエリで叔父(および叔母)を把握できます

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

于 2008-09-13T01:02:21.800 に答える