web-dev-qa-db-ja.com

MySQLで外部キーが機能しない:外部列にない値を挿入できるのはなぜですか?

MySQLでテーブルを作成しました:

CREATE TABLE actions ( A_id int NOT NULL AUTO_INCREMENT,
type ENUM('rate','report','submit','edit','delete') NOT NULL,
Q_id int NOT NULL,
U_id int NOT NULL,
date DATE NOT NULL,
time TIME NOT NULL,
rate tinyint(1),
PRIMARY KEY (A_id),
CONSTRAINT fk_Question FOREIGN KEY (Q_id) REFERENCES questions(P_id),
CONSTRAINT fk_User FOREIGN KEY (U_id) REFERENCES users(P_id));

これにより、必要なテーブルが作成されました(ただし、「DESCRIBEアクション;」コマンドを使用すると、外部キーがMULタイプのキーであることがわかり、これが何を意味するのかわかりません)。ただし、質問テーブルまたはユーザーテーブルに存在しないQ_idまたはU_idを入力しようとすると、MySQLではこれらの値が引き続き許可されます。

私は何を間違えましたか?外部キーを持つテーブルが無効なデータを受け入れないようにするにはどうすればよいですか?

更新1

最後にTYPE=InnoDBを追加すると、エラーが発生します。

エラー1005(HY000):テーブル './quotes/actions.frm'を作成できません(エラー番号:150)

なぜそれが起こるのでしょうか?

更新2

機能的な外部キーを使用してデータの整合性を強化することが重要であると言われていますが、MySQLではInnoDBを使用しないでください。何がお勧めですか?

19
stalepretzel

デフォルトのストレージエンジンはMyISAMであり、外部キーの制約を無視していると思います。外部キーの宣言を黙って受け入れますが、制約を保存したり、後で強制したりすることはありません。

ただし、外部キーに対して宣言した列に暗黙的にインデックスを作成します。 MySQLでは、「KEY」は「INDEX」の同義語です。これがDESCRIBE出力に表示されているものです。インデックスですが、制約ではありません。

制約がないため、現在、テーブルに無効な値を挿入できます。参照整合性を強制する制約を取得するには、InnoDBストレージエンジンを使用する必要があります。

CREATE TABLE actions (
  A_id int NOT NULL AUTO_INCREMENT,
  ...
  CONSTRAINT fk_Question FOREIGN KEY (Q_id) REFERENCES questions(P_id),
  CONSTRAINT fk_User FOREIGN KEY (U_id) REFERENCES users(P_id)
) ENGINE=InnoDB;

外部キー制約宣言をサイレントに無視することはMySQL側の大きな間違いだといつも思っていました。ストレージエンジンがそれらをサポートしていないというエラーや警告はありません。

CHECK制約についても同じことが言えます。ちなみに、MySQLで使用されるストレージエンジンはCHECK制約をサポートしていませんが、SQLパーサーは文句なしにそれらを受け入れます。


Errno 150の問題は、外部キー制約を理解できなかったためにInnoDBテーブルを作成できない場合に発生します。あなたはいくつかのより多くの情報を得ることができます:

SHOW ENGINE INNODB STATUS;

InnoDB外部キーのいくつかの要件:

  • 参照されるテーブルもInnoDBである必要があります。
  • 参照されるテーブルには、インデックスと主キーが必要です。
  • FK列と参照PK列のSQLデータ型は同一である必要があります。たとえば、INTはBIGINTまたはINTUNSIGNEDと一致しません。

データを含むテーブルのストレージエンジンを変更できます。

ALTER TABLE actions ENGINE=InnoDB;

これにより、MyISAMテーブル全体がInnoDBテーブルに効果的にコピーされ、成功するとMyISAMテーブルが削除され、新しいInnoDBテーブルの名前が以前のMyISAMテーブルの名前に変更されます。これは「テーブルの再構築」と呼ばれ、テーブル内のデータ量によっては時間がかかる場合があります。テーブルの再構築は、不要と思われる場合でも、ALTERTABLE中に発生します。


アップデート2について:

機能的な外部キーを使用してデータの整合性を強化することが重要であると言われていますが、MySQLではInnoDBを使用しないでください。何がお勧めですか?

誰があなたにそれを言ったの?それは絶対に間違っています。 InnoDBのパフォーマンスはMyISAMよりも優れています (ただし、InnoDBはより注意を払う必要があります 構成の調整 )、InnoDBはアトミックな変更、トランザクション、外部キーをサポートし、InnoDBは破損に対してはるかに耐性がありますクラッシュしたデータ。

サポートされていない古いバージョンのMySQL(5.0以前)を実行している場合を除き、defaultストレージエンジンの選択肢としてInnoDBを使用し、MyISAMのみを使用する必要がありますMyISAMの恩恵を受ける特定のワークロードを示すことができるかどうか。

53
Bill Karwin

私が経験した他の頭痛の時間を節約するために、giraffaが触れているように、@ FOREIGN_KEY_CHECKSが1に設定されていることを確認してください。

SELECT @@ FOREIGN_KEY_CHECKS

SET FOREIGN_KEY_CHECKS = 1

6
tttthet

このスレッドはずっと前に開かれたことを知っていますが、答えを探す将来のユーザーのためにこのメッセージを投稿しています。私はmysqlの外部キーで同じ問題を抱えていました。次のことが私のために働いた。

親テーブル:

CREATE TABLE NameSubject (
  Autonumber INT NOT NULL AUTO_INCREMENT,
  NameorSubject nvarchar(255),
  PRIMARY KEY (Autonumber)
 ) ENGINE=InnoDB;

子テーブル:

CREATE TABLE Volumes (
  Autonumber INT NOT NULL,
  Volume INT,
  Pages nvarchar(50),
  Reel int,
  Illustrations bit,
  SSMA_TimeStamp timestamp,
  Foreign KEY (Autonumber) references NameSubject(Autonumber)
  ON  update cascade 
)engine=innodb;

「ONupdatecascade」は私にとって魔法のようでした。

これが他の人にも役立つことを願っています。幸運を祈ります。

6
user203212

問題は、questions.p_idとusers.p_idがINT NOTNULLとして定義されていない可能性が高いです。外部キーが機能するには、auto_incrementとdefaultを除いて、外部キーの両側の列の定義が正確に一致している必要があります。

1
longneck

私が最初にそれを見たならば、この答えは私に多くの時間を節約したでしょう。初心者のミスの頻度で注文した次の3つの手順を試してください。

(1)「CREATETABLE」ステートメントに「ENGINE = InnoDB」を追加して、テーブルをInnodDBに変更します。

デフォルトである可能性のある他のエンジンは、外部キー制約をサポートしていませんが、サポートされていないことを通知するエラーや警告をスローすることもありません。

(2)「SETforeign_key_checks = 'ON'」を実行して、外部キー制約が実際にチェックされていることを確認します。

(3)外部キー宣言に「ONUPDATECASCADE」を追加します。

注:カスケードが目的の動作であることを確認してください。他のオプションがあります...

1
SlothOfDoom

次の記事を見つけました。現在、テストする時間がありませんが、役立つ場合があります。

http://forums.mysql.com/read.php?22,19755,43805

著者のエドウィン・ダンドは次のように述べています。

両方のテーブルがINNODBである必要があります。外部キーフィールドにはインデックスが必要です。敵のキーフィールドと参照されるフィールドは同じタイプである必要があり(私は整数のみを使用します)、何時間もの苦痛の後、それらは署名されていない必要があります。

1
stalepretzel

この問題を抱えている人の中には、MySQL用のOracle Webサイトで提供されているサンプルデータベース(sakila DBなど)から始めている人もいると思います。スクリプトの最後で「外部キー制約をオンに戻す」ことを忘れないでください(たとえば、sakila DBスクリプトの最初でそれらはオフになります)

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL';

ここでテーブルを作成します

次に、これを忘れないでください:

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;
0
bluegrass

Mysqlが外部キーの制約を無視することにまだ問題がある人、および上記の回答やその他の関連する質問でteirパズルを解決できなかった人のために、これが問題であることがわかりました。

外部キーをそのように宣言した場合

id  INTEGER UNSIGNED    REFERENCES    A_Table(id)

次に、外部キーは無視されているようです。SETコマンドを使用せずに(明らかに)制約を適用するには、次の宣言を使用します。

id  INTEGER UNSIGNED,
CONSTRAINT fk_id  FOREIGN KEY (id)  REFERENCES A_Table(id)

このようにして私は問題を解決しました。多くの人が言うように、最初の宣言は2番目のバリアントの省略形にすぎないと言う理由はわかりません。

0
user9349193413

前述のように、FK制約を適用するには、テーブルがInnoDBである必要があります。

ローカル列が外部列とは異なるタイプである外部キー制約を作成しようとしている場合にのみ、「テーブルを作成できません」に遭遇しました。

0
chaos

さて、私の推測では、「FOREIGN KEYSの作成をスキップする」オプションがチェックされています。これは、「ForwardEngineering」プロセスの「options」セクションで発生する可能性があります。

Forward Engineering - Options section

0
Issa