ON DELETE CASCADE 制約はどのような順序で処理されますか?
-
09-06-2019 - |
質問
これが私が行っていることの例です:
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;
おじと子の関係には ON DELETE CASCADE がないことに注意してください。つまりChild を削除しても、その Uncle は削除されず、その逆も同様です。
同じ子を持つ親と叔父がいて、親を削除すると、 らしい 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 は、Child を参照する Uncle よりも前に Child をカスケード削除しようとしています。
何かが足りないのでしょうか?これですか 想定 理解できない何らかの理由で失敗しますか?それともそれを機能させるための何らかのトリックがあるのでしょうか(またはMySQLのバグですか)?
解決
あなたが述べたように、親の削除は子の削除をトリガーしていますが、なぜ叔父テーブルの前に子テーブルに移動するのかわかりません。確かに知るにはdbmsコードを見る必要があると思いますが、最初にカスケードするテーブルを選択するアルゴリズムがあることは確かです。
システムは、ここで示唆しているように実際には物事を「理解」せず、制約ルールに従っているだけです。問題は、作成したスキーマがそれ以上通過できない制約に遭遇することです。
あなたの言っていることはわかります..最初に叔父テーブルにヒットした場合、レコードが削除され、次に子が削除されます (子の削除から叔父カスケードにはヒットしません)。しかし、そうであっても、実際にはそのような動作に依存するスキーマは設定されないと思います。何が起こっているのかを確実に知る唯一の方法は、コードを調べるか、ここにいる mysql/postgresql プログラマの 1 人に、fk 制約がどのように処理されるかを教えてもらうことだと思います。
他のヒント
より単純なケースでは、レコードが Child から削除され、そのレコードに参照する Uncle がある場合はどうなるでしょうか?それは指定されていないため、とにかく制約は失敗します。
Child を削除してもその Uncle は削除されない場合、代わりに何が起こるでしょうか?Uncle.childid を null にすることはできません。
あなたが望むのは、次の 3 つのうちの 1 つです。
- Uncle.childid は null にすることができ、childid には ON DELETE SET NULL が必要です。
- Uncle.childid を null にすることはできません。childid には ON DELETE CASCADE が必要です。
- Childid は Uncle に属していないため、Child と Uncle の両方に対して ON DELETE CASCADE 外部キー制約を持つ ChildsUncle リレーションが必要です。Uncleid はその関係の候補キーになります (つまり、一意である必要があります)。
@Matt Solnit まず第一に、これは本当に良い質問です。私が知っている限り、親からのレコードがいつ削除されることになるのか、innodb はまず、他のどのテーブルがそのレコードへの参照を保持しているかを特定して、それらのテーブルからレコードを削除できるようにします。良い。あなたの場合、それはChildテーブルとUncleテーブルです。この場合、最初にChildテーブルからレコードを削除することを決定したようです。したがって、Childに対して同じプロセスを繰り返し、UncleがChildテーブルへの参照を保持しているが「ON」でもないため、最終的には失敗します。 Uncleテーブルのfk_child FKに「DELETE CASCADE」または「ON DELETE SET NULL」が指定されています。ただし、どうやら もし innodb は最初に Uncle テーブルからレコードを削除しようとします。その後、削除はスムーズに行われるはずです。よくよく考えてみると、innodb は ACID モデルに従っているため、削除プロセスの開始に Uncle ではなく Child を選択しているのだと思います。これは、Uncle で開始した場合でも Child の削除がまだ失敗している可能性があるためです。ON DELETE CASCADE を使用しないで、fk_child キー (Uncle に似た) を持つ Friend テーブルを想定すると、これでもトランザクション全体が失敗する原因となるため、これは私にとっては正しい動作のように見えます。言い換えれば、innodb はトランザクションの失敗を引き起こす可能性のあるテーブルから始まりますが、これは私の理論であり、実際にはまったく異なる話になる可能性があります。:)
デザインが全部間違ってる。(文字通り) 親子関係を持つ単一のテーブルが必要です。次に、クエリを使用して叔父 (および叔母) を特定できます。
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