外部キー制約でのON DELETE CASCADE
オプションの使い方を具体的な例を使って解説します。
ON DELETE CASCADE
オプションは親テーブル(参照先のテーブル)を削除するときに子テーブル(参照元のテーブル)も一緒に削除するために使用するオプションになります。
解説に使用するデータベースとテーブルについて
解説に使用するデータベースとテーブルについて説明します。
確認にはOracle Database 19cを使用しています。若干の構文の違いはありますが、他の主要なデータベースでも同様の操作が可能です。
まずは親テーブルを作成します。親テーブルのMAINテーブルは主キーIDのみを持ったテーブルです。
CREATE TABLE MAIN
(
ID NUMBER(3),
CONSTRAINT MAIN_PK PRIMARY KEY (ID)
)
次に子テーブルを作成します。子テーブルのSUBテーブルはIDとSEQを主キーに持つテーブルです。
CREATE TABLE SUB
(
ID NUMBER(3),
SEQ NUMBER(2),
CONSTRAINT SUB_PK PRIMARY KEY (ID, SEQ)
)
ON DELETE CASCADE オプションなしの場合
では、ON DELETE CASCADE
オプションを使用しない場合の動作を見てみましょう。
SUBテーブルのIDに外部キー制約を設定します。
SQL> ALTER TABLE SUB ADD CONSTRAINT SUB_ID_FK
2 FOREIGN KEY (ID) REFERENCES MAIN(ID)
表が変更されました。
では、データを投入して削除時の動作を確認します。
SQL> INSERT INTO MAIN VALUES (1)
1行が作成されました。
SQL> INSERT INTO SUB VALUES (1, 1)
1行が作成されました。
SQL> INSERT INTO SUB VALUES (1, 2)
1行が作成されました。
削除する前にデータを確認しておきましょう。
SQL> SELECT * FROM MAIN
ID
--
1
1行が選択されました。
SQL> SELECT * FROM SUB
ID SEQ
-- ---
1 1
1 2
2行が選択されました。
MAINテーブルのID=1のレコードにSUBテーブルのID=1のレコードが2件ぶら下がっている形になっています。
データが投入できましたので、親テーブルMAINテーブルのレコードを削除してみます。
SQL> DELETE FROM MAIN WHERE ID = 1
DELETE FROM MAIN WHERE ID = 1
*
行1でエラーが発生しました。:
ORA-02292: 整合性制約(USER01.SUB_ID_FK)に違反しました - 子レコードがあります
整合性制約(外部キー制約)で削除に失敗しました。ON DELETE CASCADE
オプションがない場合は、子テーブルを削除してからでないと親テーブルを削除できません。
念のためデータが削除されていないことを確認しておきます。
SQL> SELECT * FROM MAIN
ID
--
1
1行が選択されました。
SQL> SELECT * FROM SUB
ID SEQ
-- ---
1 1
1 2
2行が選択されました。
ON DELETE CASCADEオプションありの場合
では、ON DELETE CASCADE
オプションをつけた場合の削除の動きを確認します。
すでに設定済みの外部キーを削除しておきます。
SQL> ALTER TABLE SUB DROP CONSTRAINT SUB_ID_FK
表が変更されました。
今度はON DELETE CASCADE
オプション付きで外部キー制約を設定します。
SQL> ALTER TABLE SUB ADD CONSTRAINT SUB_ID_FK
2 FOREIGN KEY (ID) REFERENCES MAIN(ID)
3 ON DELETE CASCADE
表が変更されました。
では、もう一度削除を試します。
> DELETE FROM MAIN WHERE ID = 1
1行が削除されました。
今度は削除できました。
削除後のデータを確認します。
SQL> SELECT * FROM MAIN
レコードが選択されませんでした。
SQL> SELECT * FROM SUB
レコードが選択されませんでした。
削除したMAINテーブルのレコードだけでなく、SUBテーブルのID=1のレコードも削除されています。
どういった場合にON DELETE CASCADEオプションを使うか
まず、外部キー制約を使用する場合に限られているということが前提となります。
上記の前提の上で、以下の2つのどちらの効果を狙いたいかで決めるとよいでしょう。
- 子テーブルにデータがあるときに親テーブルが削除されるのを防ぎたい
- 親テーブルが削除されるときに子テーブルをまとめて削除したい
もう少し具体的な例で説明します。
ON DELETE CASCADEオプションを使いたくないケース
ON DELETE CASCADE
オプションを使いたくないケース、使うべきでないケースとして挙げられるのが、マスタテーブルに対して外部キー制約を設定しているケースです。
例えば、注文テーブルにある顧客番号に対して、顧客マスタの顧客番号への外部キーを設定している場合、ON DELETE CASCADEオ
プションを設定していると、注文テーブルのレコードの削除によって顧客マスタのレコードが削除される場合があります。
これは望ましい挙動でないことから、マスタテーブルへの外部キー設定の場合はON DELETE CASCADE
オプションを使用するべきではありません。
ON DELETE CASCADE オプションを使いたいケース
ON DELETE CASCADE
オプションを使いたいケースとして挙げられるのが、子テーブルが親テーブルに対して外部キー制約を設定しているケースです。
例えば、注文テーブルと注文明細テーブルがあり、注文明細の注文番号に対して、注文テーブルの注文番号への外部キーを設定している場合は、ON DELETE CASCADE
オプションを設定していると、注文テーブルのレコード削除時に注文明細テーブルのレコードをまとめて削除してくれるため便利です。ON DELETE CASCADE
オプションを設定していない場合は、先に注文明細テーブルの該当する注文番号のレコードを先に削除してからでないと注文テーブルのレコードを削除できないため、ON DELETE CASCADE
オプションを使うことが削除を行うプログラムの構造をすっきりとさせることができる効果があります。
まとめ
前述の例をまとめると以下のようになります。
CREATE TABLE 注文
(
注文番号 NUMBER(10),
顧客番号 NUMBER(3),
CONSTRAINT 注文_PK PRIMARY KEY (ID)
)
CREATE TABLE 注文明細
(
注文番号 NUMBER(10),
注文明細連番 NUMBER(10),
CONSTRAINT 注文明細_PK PRIMARY KEY (注文番号, 注文明細番号)
)
CREATE TABLE 顧客マスタ
(
顧客番号 NUMBER(3),
CONSTRAINT 顧客マスタ_PK PRIMARY KEY (顧客番号)
-- 注文から顧客マスタへの外部キーではON DELETE CASCADEオプションをつけない
ALTER TABLE SUB ADD CONSTRAINT 注文_顧客番号_FK
FOREIGN KEY (顧客番号) REFERENCES 顧客マスタ(顧客番号)
-- 注文明細から注文への外部キーではON DELETE CASCADEオプションをつける
ALTER TABLE SUB ADD CONSTRAINT 注文明細_注文番号_FK
FOREIGN KEY (注文番号) REFERENCES 注文(注文番号)
ON DELETE CASCADE
このようにON DELETE CASCADE
オプションを適切に設定する/しないことでデータを安全に、効率よく管理できるようになります。