4

foreign_key_checks0に設定されている間にInnoDBテーブルに挿入された後、単一の行の参照整合性をチェックする方法があるかどうかを確認しようとしています.

したがって、次の 2 つのテーブルが与えられます。

CREATE TABLE `book` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  `author_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `book_cc846901` (`author_id`),
  CONSTRAINT `author_id_refs_id_7fdd0933` FOREIGN KEY (`author_id`) REFERENCES `person` (`id`)
) ENGINE=InnoDB;


CREATE TABLE `person` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB;

それらに対して次のステートメントを実行します。

SET foreign_key_checks=0
INSERT INTO `book` (`id`, `name`, `author_id`) VALUES (1, 'Cryptonomicon', 3)
INSERT INTO `person` (`id`, `name`) VALUES (4, 'Neal Stephenson')
SET foreign_key_checks=1

ここにいくつかの悪いデータを入れました -- author_id は存在しない person.id を参照していますこのように。

私が最初に取ろうとしていたアプローチは、まったく同じデータで行を更新することでした。私はこれを試しました:

UPDATE `book` SET `name` = 'Cryptonomicon', `author_id` = 3 WHERE `book`.`id` = 1 

これによりエラーが発生すると予想していましたが、そうではありませんでした。データが変更されていないため、明らかに整合性チェックは行われていませんか? たとえば、 author_id を 2 に設定すると失敗します

私の質問: 特定の行を検査し、問題がある場合に整合性エラーをトリガーできる代替手段はありますか? 私は周りをグーグルで検索しましたが、何も見つかりませんでした。テーブル全体をチェックすることは避けたいのですが、それが唯一のルートかもしれません。

私を逃れている洞察やその他のアプローチをいただければ幸いです。

(なぜ私がこれをすべてやろうとしているのか知りたいのであれば、それは、前方参照を持つフィクスチャをロードできないという Django フレームワークで未解決の問題を解決しようとしているからです。提案された解決策は、外部キー チェックを無効にすることです。フィクスチャがロードされ、ロードが完了した後に再び有効になったとき私は、その時点で戻って、外部キーチェックがオフのときに追加された行の参照整合性をチェックし、エラーを発生させる方法を理解しようとしています。問題がある場合はエラーになります。)

4

2 に答える 2

8

この質問の答えのおかげで実際に得た解決策を思いつきました:テーブル/テーブルの外部キーを強制的に InnoDB に再チェックさせますか?

私は以前にその質問を見たことがありますが、最終的に約 20 分かけてすべての抽象化を展開し、具体的に何をしているのかを理解しようとしました。実際にはそれほど複雑ではありません。この質問で示した例の観点からソリューションの核心を表現すると、基本的に 2 つの SELECT ステートメントがあります。

SELECT *
FROM information_schema.KEY_COLUMN_USAGE
WHERE
    `CONSTRAINT_SCHEMA` LIKE `test`
    AND `TABLE_NAME` = `book`

これにより、テーブル内のすべてのキー列が返されbookます。これらの結果を反復して、次のように参照を確認できます。この例では、「author_id」を使用しています。

SELECT * FROM `book` as REFERRING
LEFT JOIN `person` as REFERRED
ON (REFERRING.`author_id` = REFERRED.`id`)
WHERE REFERRING.`author_id` IS NOT NULL
AND REFERRED.`id` IS NULL

基本的に、このステートメントは、author_id に値があり、関連するテーブルに id に対応する値がない行を返します。これらは「不良行」です。

MySQL でエラーを直接トリガーする必要はありませんでした。この目的のために、アプリケーション コードでエラーを発生させることができます。しかし、これはかなり簡単で迅速な解決策になったので、見つけてよかったです!

于 2011-06-24T20:58:02.700 に答える
1

何も変更されていないため、MySQLが更新を無視したため、UPDATEトリックは機能しませんでした。

UPDATE `book` SET
  `name` = 'Cryptonomicon',
  `author_id` = 3
WHERE `book`.`id` = 1;
Query OK, 0 rows affected (0.03 sec)
Rows matched: 1  Changed: 0  Warnings: 0

したがって、最初に別の値に更新してから元の値に戻すことができます。これにより、目的のエラーがスローされます。データベースに影響を与えずにロールバックできるように、すべてをトランザクションでラップしました。

START TRANSACTION;

SET foreign_key_checks=0;

INSERT INTO `book` (`id`, `name`, `author_id`)
VALUES (1, 'Cryptonomicon', 3);

SET foreign_key_checks=1;

INSERT INTO `person` (`name`) VALUES ('NULL');

UPDATE `book` SET
  `author_id` = LAST_INSERT_ID()
WHERE `book`.`id` = 1;

UPDATE `book` SET
  `author_id` = 3
WHERE `book`.`id` = 1;

ERROR 1452 (23000): Cannot add or update a child row: a foreign key constraint fails (`test`.`book`, CONSTRAINT `author_id_refs_id_7fdd0933` FOREIGN KEY (`author_id`) REFERENCES `person` (`id`))

ROLLBACK;

厄介で潜在的に危険な解決策のように見えますが、元の問題については十分にわかりません...実際に元の値をテーブルに保持したいかどうかもわかりません。その場合、リセットするには、外部キー制約を再度無効にする必要があります。

于 2011-06-24T20:16:51.490 に答える