MySQL道普請便り
第63回 MySQLの外部キー(Foreign Key)制約
MySQLではデータベースの一貫性を保つため,
外部キーの作成
はじめに,CREATE TABLE
やALTER TABLE
構文で作成できます。例としてt0
テーブルを親テーブル,t1
テーブルを子テーブルでuser_
カラムを外部キーとして設定してみます。下記,t1
テーブルのCREATE構文のCONSTRAINT以下が外部キー作成のための構文になります。
mysql> CREATE TABLE t0 ( user_id int primary key ) ENGINE=INNODB; mysql> CREATE TABLE t1 ( id int primary key , user_id int , update_time datetime , KEY idx_userid (user_id), CONSTRAINT fk_t0_userid FOREIGN KEY fk_userid(user_id) REFERENCES t0 (user_id) ON DELETE RESTRICT ON UPDATE RESTRICT ) ENGINE=INNODB;
CONSTRAINT fk_
は制約名です。ここを省略した場合は自動で制約名が作成されます。この制約名はデータベース単位で一意の値になります。t0_ userid FOREIGN KEY fk_
は,userid(user_ id) 外部キーIDと外部キーとなるカラムを指定します。 REFERENCES t0 (user_
は,id) 参照される親テーブルとカラムを指定します。 ON DELETE RESTRICT ON UPDATE RESTRICT
は,参照アクションです。
参照アクション
参照アクションは,
アクション | ON UPDATE | ON DELETE |
---|---|---|
RESTRICT | 親テーブルへのUPDATEは拒否される | 親テーブルへのDELETEは拒否される |
NO ACTION | RESTRICTと同様 | RESTRICTと同様 |
CASCADE | 親テーブルへのUPDATEが実施され, | 親テーブルへのDELETEが実施され, |
SET NULL | 親テーブルへのUPDATEが実施され, | 親テーブルへのDELETEが実施され, |
外部キー作成の条件
外部キーを作成するにはいくつかの条件があります。
- 親テーブルと子テーブルは同じストレージエンジンでなければならない
- 親テーブルの参照されるキーと子テーブルの外部キーは,
それぞれ同様のデータ型でなければならない - 親テーブルの参照されるキーと子テーブルの外部キーにはインデックスが必要
- InnoDBのパーティションテーブルは,
外部キーがサポートされていない
3.については,
子テーブルにインデックスが存在しない場合,外部キー作成と同時に暗黙でインデックスが作成される
その時のインデックス名は,
- 制約名
- 1が記述がない場合,
外部キーID - 1と2のどちらの記述もない場合,
外部キーに指定されたカラム名
インデックスプレフィックスはサポートされない
インデックスプレフィックスとは,
外部キー制約を確認
以下のテーブルから外部キーの関連情報を確認できます。
- INFORMATION_
SCHEMA. KEY_ COLUMN_ USAGE - INFORMATION_
SCHEMA. INNODB_ SYS_ FOREIGN - INFORMATION_
SCHEMA. INNODB_ SYS_ FOREIGN_ COLS
また,SHOW ENGINE INNODB STATUS
のLATEST FOREIGN KEY ERROR
セクションから最新の外部キー制約エラーに関する情報を確認することができます。
暗黙で作成されたインデックスにおける注意点
前述にあるように,
しかし,
まずは,t1
テーブルからインデックス作成部分を除外して実行します。
mysql> CREATE TABLE t1 ( id int primary key , user_id int , update_time datetime , CONSTRAINT fk_t0_userid FOREIGN KEY fk_userid(user_id) REFERENCES t0 (user_id) ) ENGINE=INNODB;
SHOW CREATE TABLE
構文で確認します。
mysql> SHOW CREATE TABLE t1\G Table: t1 Create Table: CREATE TABLE `t1` ( `id` int(11) NOT NULL, `user_id` int(11) DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `fk_t0_userid` (`user_id`), CONSTRAINT `fk_t0_userid` FOREIGN KEY (`user_id`) REFERENCES `t0` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
KEY fk_
という前述のルールにより制約名でインデックスが作成されました。
ここに(user_
mysql> ALTER TABLE t1 ADD KEY idx_userid_updatetime(user_id,update_time); Query OK, 0 rows affected (0.00 sec) Records: 0 Duplicates: 0 Warnings: 0 mysql> SHOW CREATE TABLE t1\G Table: t1 Create Table: CREATE TABLE `t1` ( `id` int(11) NOT NULL, `user_id` int(11) DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `idx_userid_updatetime` (`user_id`,`update_time`), CONSTRAINT `fk_t0_userid` FOREIGN KEY (`user_id`) REFERENCES `t0` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
fk_
のインデックスが削除されました。ここでの注意として,
USEまたはFORCE INDEXを使用したクエリ
既存のクエリにUSEまたはFORCE INDEXで先ほど暗黙で作成されたインデックスを指定していた場合,
mysql> SELECT * FROM t1 USE INDEX (fk_t0_userid); ERROR 1176 (42000): Key 'fk_t0_userid' doesn't exist in table 't1'
MySQL5.
mysql> SHOW CREATE TABLE t1\G Table: t1 Create Table: CREATE TABLE `t1` ( `id` int(11) NOT NULL, `user_id` int(11) DEFAULT NULL, `update_time` datetime DEFAULT NULL, PRIMARY KEY (`id`), KEY `fk_t0_userid` (`user_id`), CONSTRAINT `fk_t0_userid` FOREIGN KEY (`user_id`) REFERENCES `t0` (`user_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 mysql> ALTER TABLE t1 ADD KEY `fk_t0_userid` (`user_id`); Query OK, 0 rows affected (0.01 sec) Records: 0 Duplicates: 0 Warnings: 0
しかし,SHOW CREATE TABLE
構文と同様)
mysql> SHOW SLAVE STATUS¥G Slave_IO_Running: Yes Slave_SQL_Running: No Last_Errno: 1061 Last_Error: Error 'Duplicate key name 'fk_t0_userid'' on query. Default database: 't'. Query: 'ALTER TABLE t1 ADD KEY `fk_t0_userid` (`user_id`)'
結論としては,
- 外部キー作成する際は予期せぬエラーを防ぐために明示的にインデックスを作成する
- インデックスの命名規則を定めるようにして,
暗黙で作成されたインデックスは命名規則と異なるので気づけるようにする
バックナンバー
MySQL道普請便り
- 第171回 MyDumperを使ってみよう[その2]
- 第170回 MySQLのキーワードと予約語をテーブルに使用したい場合
- 第169回 pt-variable-advisorを使ってみよう
- 第168回 MyDumperを使ってみよう[その1]
- 第167回 M1搭載MacのDockerでMySQLを動かしてみる
- 第166回 Performance_schema関数
- 第165回 MySQLの圧縮いろいろ
- 第164回 admin_address,admin_portを設定しよう
- 第163回 sysbench 1.0を使ってMySQLの負荷テストをする
- 第162回 mysqlクライアントの履歴を確認してみる