13

以下のテーブルが 2 つあるとします。

CREATE TABLE post (
  id bigint(20)     NOT NULL    AUTO_INCREMENT,
  text text ,

  PRIMARY KEY (id)
) ENGINE=InnoDB AUTO_INCREMENT=1;

CREATE TABLE post_path (
  ancestorid bigint(20)     NOT NULL    DEFAULT '0',
  descendantid bigint(20)   NOT NULL    DEFAULT '0',
  length int(11)            NOT NULL    DEFAULT '0',

  PRIMARY KEY (ancestorid,descendantid),
  KEY descendantid (descendantid),

  CONSTRAINT f_post_path_ibfk_1 
    FOREIGN KEY (ancestorid) REFERENCES post (id) 
    ON DELETE CASCADE 
    ON UPDATE CASCADE,
  CONSTRAINT f_post_path_ibfk_2 
    FOREIGN KEY (descendantid) REFERENCES post (id) 
    ON DELETE CASCADE 
    ON UPDATE CASCADE
) ENGINE=InnoDB;

そして、これらの行を挿入しました:

INSERT INTO 
    post (text)
    VALUES ('a'); #// inserted row by id=1
INSERT INTO 
    post_path (ancestorid ,descendantid ,length) 
    VALUES (1, 1, 0);

投稿の行 ID を更新したい場合:

 UPDATE post SET id = '10' WHERE post.id =1

MySQL は次のように述べています。

#1452 - Cannot add or update a child row: a foreign key constraint fails (test.post_path, CONSTRAINT f_post_path_ibfk_2 FOREIGN KEY (descendantid) REFERENCES post (id) ON DELETE CASCADE ON UPDATE CASCADE) 

なんで?なにが問題ですか?

編集:

これらの行を挿入したとき:

INSERT INTO 
    post (text)
    VALUES ('b'); #// inserted row by id=2

INSERT INTO 
    post_path (ancestorid, descendantid, length)
    VALUES (1, 2, 0);

そして更新されました:

UPDATE post SET id = '20' WHERE post.id =2

Mysql は、子行と親行の両方を正常に更新しました。最初の投稿 (id=1) を更新できないのはなぜですか?

4

4 に答える 4

6

OK、私もアクセスできるテストデータベースでスキーマとクエリを実行しましたが、次のことに気付きました。両方の行を両方のテーブルに挿入した後、更新前のデータは次のようになります。

mysql> select * from post;
+----+------+
| id | text |
+----+------+
|  1 | a    |
|  2 | b    |
+----+------+
2 rows in set (0.00 sec)

mysql> select * from post_path;
+------------+--------------+--------+
| ancestorid | descendantid | length |
+------------+--------------+--------+
|          1 |            1 |      0 |
|          1 |            2 |      0 |
+------------+--------------+--------+
2 rows in set (0.00 sec)

update ステートメントを発行した後、post.id を 20 に更新します。

mysql> UPDATE `post` SET `id` = '20' WHERE `post`.`id` =2;
Query OK, 1 row affected (0.08 sec)
Rows matched: 1  Changed: 1  Warnings: 0

mysql> select * from post_path;
+------------+--------------+--------+
| ancestorid | descendantid | length |
+------------+--------------+--------+
|          1 |            1 |      0 |
|          1 |           20 |      0 |
+------------+--------------+--------+
2 rows in set (0.00 sec)

祖先 ID がまだ 1 であることに注意してください。これはMySQLの問題のようです。

外部キー制約がある InnoDB テーブルを含む複数テーブルの UPDATE ステートメントを使用する場合、MySQL オプティマイザーは、親子関係とは異なる順序でテーブルを処理する可能性があります。この場合、ステートメントは失敗し、ロールバックします。代わりに、単一のテーブルを更新し、 InnoDB が提供する ON UPDATE 機能に依存して、それに応じて他のテーブルを変更します。セクション14.3.5.4「InnoDB および FOREIGN KEY 制約」を参照してください。

最初のクエリが失敗する理由は、先祖 ID が 10 に更新されていないが、子孫 ID が更新されているためであり、post.id を 10 に設定しようとしていて、post_path テーブルの先祖 ID がまだ値 1 を参照しているためです。もはや存在します。

これを回避するためにスキーマを変更し、衝突を避けるために auto_increment 列の更新も回避することを検討する必要があります。

于 2013-04-12T20:21:54.140 に答える
0

2 番目の方法が機能した主な理由は、 と に異なる値を保持したことancestoridですdescendantid。特定の属性の変更に基づいて 2 つの異なる制約を作成する場合。最初の制約のみが機能し、2 番目の制約は機能しません。これは、最初の更新試行の場合です。

于 2013-05-04T17:01:24.997 に答える