web-dev-qa-db-ja.com

外部キーの制約:ON UPDATEおよびON DELETEを使用する場合

私はMySQL Workbenchを使用してデータベーススキーマを設計していますが、これはダイアグラムを作成して変換できるため、かなりクールです:P

とにかく、InnoDBを使用することにしました。これは、外部キーのサポートのためです。私が気づいたことの1つは、外部キーの更新時および削除時のオプションを設定できることです。誰かが簡単な例で「制限」、「カスケード」、およびnullを設定できる場所を説明できますか?

たとえば、userを含むuserIDテーブルがあるとします。そして、2つの外部キー(同じ主キー、messageテーブルのuserIDを参照する)を持つ多対多のメッセージテーブルuserがあるとします。この場合、[更新時]および[削除時]オプションの設定は役に立ちますか?その場合、どれを選択しますか?これが良い例ではない場合、これらがどのように役立つかを説明するための良い例を考えてください。

ありがとう

169
meltuhamy

データベースに制約を加えることをheしないでください。一貫性のあるデータベースが必ず得られますが、それがデータベースを使用する良い理由の1つです。特に、複数のアプリケーションがそれを要求している場合(または、1つのアプリケーションだけで、異なるソースを使用するダイレクトモードとバッチモードがある場合)。

MySQLでは、postgreSQLのような高度な制約はありませんが、少なくとも外部キー制約は非常に高度です。

例として、これらの会社の人を含むユーザーテーブルを持つ会社テーブルを取り上げます。

CREATE TABLE COMPANY (
     company_id INT NOT NULL,
     company_name VARCHAR(50),
     PRIMARY KEY (company_id)
) ENGINE=INNODB;

CREATE TABLE USER (
     user_id INT, 
     user_name VARCHAR(50), 
     company_id INT,
     INDEX company_id_idx (company_id),
     FOREIGN KEY (company_id) REFERENCES COMPANY (company_id) ON...
) ENGINE=INNODB;

ON UPDATE句を見てみましょう:

  • ON UPDATE RESTRICTdefault:テーブルCOMPANYのcompany_idを更新しようとすると、エンジンは1人のユーザーが少なくともこの会社にリンクしている場合、操作を拒否します。
  • ON UPDATE NO ACTION:RESTRICTと同じです。
  • ON UPDATE CASCADE最適なもの:テーブルCOMPANYの行のcompany_idを更新する場合エンジンは、このCOMPANYを参照するすべてのUSER行でそれに応じて更新します(ただし、USERテーブルでトリガーがアクティブ化されない、警告)。エンジンはあなたのために変更を追跡します、それは良いことです。
  • ON UPDATE SET NULL:テーブルCOMPANYの行のcompany_idを更新する場合、エンジンは関連するユーザーのcompany_idをNULLに設定します(USER company_idで使用可能になります)フィールド)。アップデートでそれと関係のある面白いことは見当たらないが、間違っているかもしれない。

そして今、ON DELETE側:

  • ON DELETE RESTRICTthe default:テーブルCOMPANY会社のcompany_id Idを削除しようとした場合この会社の少なくとも1人のユーザーがあなたの命を救うことができる場合、操作を拒否します。
  • DELETE NO ACTION:RESTRICTと同じ
  • ON DELETE CASCADEdangerous:テーブルCOMPANYの会社行を削除すると、エンジンは関連するユーザー。これは危険ですが、セカンダリテーブルで自動クリーンアップを実行するために使用できます(したがって、必要な場合もありますが、COMPANY <-> USERの例ではそうではありません)
  • ON DELETE SET NULLhandful:COMPANY行を削除すると、関連するユーザーは自動的にNULLとの関係。 Nullが会社のないユーザーにとって価値がある場合、これは良い動作です。たとえば、コンテンツの作成者として、ユーザーをアプリケーション内に保持する必要があるかもしれませんが、会社を削除しても問題はありません。

通常、デフォルトはON DELETE RESTRICT ON UPDATE CASCADEです。いくつかのON DELETE CASCADEをトラックテーブル(すべてのログではなく、ログなど)に使用し、マスターテーブルがUSERテーブルのJOBテーブルのような外部キーを含むテーブルの「単純な属性」の場合にON DELETE SET NULLを使用します。

編集

それを書いてから久しぶりです。ここで、重要な警告を1つ追加する必要があります。 MySQLには、カスケードに関する1つの大きな文書化された制限があります。 カスケードはトリガーを起動していません。そのため、そのエンジンでトリガーを使用するのに十分自信がある場合は、カスケード制約を回避する必要があります。

MySQLトリガーは、SQLステートメントによってテーブルに加えられた変更に対してのみアクティブになります。ビューの変更や、SQLステートメントをMySQLサーバーに送信しないAPIによって行われたテーブルの変更に対してはアクティブになりません。

==>以下の最後の編集を参照してください、物事はこのドメインで動いています

トリガーは、外部キーアクションによってアクティブ化されません。

そして、私はこれがいつか修正されるとは思わない。外部キー制約はInnoDbストレージによって管理され、トリガーはMySQL SQLエンジンによって管理されます。両方が分離されています。 Innodbは制約管理を備えた唯一のストレージです。たぶん、いつかストレージエンジンに直接トリガーを追加するかもしれません。

しかし、貧弱なトリガー実装と非常に有用な外部キー制約サポートのどちらを選択すべきかについて、私は自分の意見を持っています。そして、データベースの一貫性に慣れると、PostgreSQLが好きになります。

12/2017-MySQLに関するこの編集の更新:

@IstiaqueAhmedがコメントで述べたように、この主題の状況は変わりました。そのため、リンクをたどって、実際の最新の状況を確認してください(将来、再び変わる可能性があります)。

441
regilero

@MarkR回答への追加-注意すべきことの1つは、ORMを使用する多くのPHPフレームワークが高度なDBセットアップ(外部キー、カスケード削除、一意制約)を認識または使用しないため、予期しない動作が発生する可能性があることです。

たとえば、ORMを使用してレコードを削除し、DELETE CASCADEが関連テーブルのレコードを削除する場合、ORMがこれらの関連レコードを削除しようとすると(多くの場合自動)エラーが発生します。

2
lxa

これは、アプリケーションのコンテキストで考慮する必要があります。一般に、データベースではなくアプリケーションを設計する必要があります(データベースは単にアプリケーションの一部です)。

アプリケーションがさまざまなケースにどのように応答するかを検討してください。

デフォルトのアクションは、操作を制限する(許可しない)ことです。これは通常、愚かなプログラミングエラーを防ぐために必要な操作です。ただし、DELETEではCASCADEも役立ちます。それは実際にアプリケーションと特定のオブジェクトを削除する方法に依存します。

個人的に、InnoDBを使用するのは、FK制約があるためというよりも、データを破壊しないためです(MyISAMを破壊します)。

1
MarkR