冒頭でインストールしたsample_db5データベースでは、すでに外部キー制約の設定が完了しています。
 さっそく試しに、親テーブルであるitemのidカラム値のアップデートを行ってみましょう。親に対して行った修正が、子側にも反映されていることが確認できます。
  
    mysql> SELECT * FROM item; ←更新前の状態を確認 
      
        +----+------------------------------------+--------------------------------------------
| id | name                               | detail
 
+----+------------------------------------+-------------------------------------------- 
|  1 | 半熟卵のMySQL風チキンドリア        | チキンたっぷりのリゾットに半熟卵をトッピン 
|  2 | 地鶏とキノコのBIND9風フェットチーネ| きしめん状の幅広パスタがBIND9の堅牢さによく 
|  3 | 有機野菜のApache風グリーンサラダ   | 有機野菜の鮮度そのままに,酸味を抑えたドレ 
+----+------------------------------------+-------------------------------------------- 
(※表示を一部省略しています) 
    
       
      mysql> SELECT * FROM order_item; ←更新前の状態を確認 
          +------------+---------------------+------+-----+
| order_no   | order_date          | item | qty | 
+------------+---------------------+------+-----+ 
| 1000000000 | 2007-06-28 20:45:46 |    1 |   1 | 
| 1000000000 | 2007-06-28 20:45:46 |    2 |  16 | 
| 1000000000 | 2007-06-28 20:45:46 |    3 |   4 | 
+------------+---------------------+------+-----+ 
      
         
        mysql> UPDATE item SET id = '5' WHERE id = '1';  
                     ←「半熟卵のMySQL風チキンドリア」のidを「5」に変更(「親」側の更新) 
         mysql> SELECT * FROM item; ←更新後の状態を確認 
         
+----+------------------------------------+--------------------------------------------
| id | name                               | detail 
+----+------------------------------------+-------------------------------------------- 
|  2 | 地鶏とキノコのBIND9風フェットチーネ| きしめん状の幅広パスタがBIND9の堅牢さによく 
|  3 | 有機野菜のApache風グリーンサラダ   | 有機野菜の鮮度そのままに,酸味を抑えたドレ 
|  5 | 半熟卵のMySQL風チキンドリア        | チキンたっぷりのリゾットに半熟卵をトッピン 
|    |                                    |                 ←idが1から5へ変更されている 
+----+------------------------------------+--------------------------------------------  
(※表示を一部省略しています) 
 
        mysql> SELECT * FROM order_item;    ←子側の状態を確認 
+------------+---------------------+------+-----+
| order_no   | order_date          | item | qty | 
+------------+---------------------+------+-----+ 
| 1000000000 | 2007-06-28 20:45:46 |    2 |  16 | 
| 1000000000 | 2007-06-28 20:45:46 |    3 |   4 |  
| 1000000000 | 2007-06-28 20:45:46 |    5 |   1 | 
|                               ←itemが1から5へ変更されている 
+------------+---------------------+------+-----+ 
 | 
  
 親側のデータを削除しようとした場合、子側にデータが残っていれば、外部キー制約によってエラーになります。
  
    mysql> DELETE FROM item WHERE id = '3'; 
      ERROR 1451 (23000): Cannot delete or update a parent row: a foreign key constraint fails (`sample_db5/order_item`, CONSTRAINT `fk_2` FOREIGN KEY (`item`) REFERENCES `item` (`id`) ON UPDATE CASCADE) 
       
      ※MySQLがエラー番号「1451」を出力している点に注目します。 
      次回紹介するサンプル「ItemDao.php」で利用します |   
  
 外部キー制約ではまた、更新や削除の際の動作パターンを設定することもできます。
 先ほど、親側のitemテーブルのデータを削除した際、子側に同じデータが残っているとエラーが生じ、削除を行うことができませんでした。
 今度は、もう1つの親側のorder_mainテーブルのデータの削除を試みます。order_itemテーブルのorder_noカラムは、order_mainテーブルのorder_noカラムを親に持つよう外部キー制約を設定しています。
  
    mysql> SELECT * FROM order_main; ←更新前の状態を確認 
      
        +------------+---------------------+--------------------------+----------+------
| order_no   | order_date          | name                     | zip_code | addre 
+------------+---------------------+--------------------------+----------+------ 
| 1000000000 | 2007-06-28 20:45:46 | アイティメディア         | 1000005  | 東京 
+------------+---------------------+--------------------------+----------+------       
(※表示を一部省略しています) 
       
      mysql> SELECT * FROM order_item; ←更新前の状態を確認 
      +------------+---------------------+------+-----+
       
      | order_no   | order_date          | item | qty | 
+------------+---------------------+------+-----+ 
| 1000000000 | 2007-06-28 20:45:46 |    2 |  16 | 
| 1000000000 | 2007-06-28 20:45:46 |    3 |   4 | 
| 1000000000 | 2007-06-28 20:45:46 |    5 |   1 | 
+------------+---------------------+------+-----+
 
       
      mysql> DELETE FROM order_main WHERE order_no = "1000000000";  
                       ←親側を削除 
      mysql> SELECT * FROM order_main; ←親側の削除を確認 
      Empty set (0.00 sec) 
      mysql> SELECT * FROM order_item; ←子側も同時に削除されている 
      Empty set (0.00 sec) | 
    
 今度は、親側の削除と同時に子側も削除されていることが分かります。
 こうした外部キーの動作パターンは、以下のように、子側のorder_itemテーブル構築時に設定します。
  
    mysql> SHOW CREATE TABLE order_item; 
      CREATE TABLE `order_item` ( 
  `order_no` int(10) unsigned NOT NULL, 
  `order_date` datetime NOT NULL, 
  `item` int(10) unsigned NOT NULL, 
  `qty` int(10) NOT NULL, 
  UNIQUE KEY `uk1` (`order_no`,`item`), 
  KEY `item` (`item`), 
  CONSTRAINT `fk_1` FOREIGN KEY (`order_no`) REFERENCES `order_main` (`order_no`) ON DELETE CASCADE ON UPDATE CASCADE, 
  CONSTRAINT `fk_2` FOREIGN KEY (`item`) REFERENCES `item` (`id`) ON UPDATE CASCADE 
) ENGINE=InnoDB DEFAULT CHARSET=utf8 
(※表示を一部省略しています) | 
 まず「CONSTRAINT....CASCADE」で外部キー制約を設定しています。「fk_1」がorder_noカラムとorder_mainテーブルのorder_noカラムとの外部キー制約、「fk_2」がitemカラムとitemテーブルのidカラムとの外部キー制約になります。
 fk_1の後半では「ON DELETE CASCADE ON UPDATE CASCADE」で削除(ON DELETE)時に「CASCADE」、更新(ON UPDATE)時にもCASCADEが設定されています。CASCADEを指定することで、更新時には親側の更新に合わせて子側も同様の値で更新を行い、削除時には親側の削除に合わせ該当する子側のデータも削除されます。
 fk_2では「ON UPDATE CASCADE」で更新時にはCASCADEが設定されていますが、削除(ON DELETE)時の動作は設定されていません。この場合、デフォルトの「RESTRICT」が適用されます。RESTRICTでは、子側にデータが残っていて親側のデータを削除しようとした場合、エラーになります。
  
    CONSTRAINT `制約名` FOREIGN KEY (`子側カラム名`) 
    REFERENCES `親側テーブル名` (`親側カラム名`)  
    ON DELETE 削除時の動作 ON UPDATE 更新時の動作
  | 
 CASCADEやRESTRICTのほかに「SET NULL」も用意されています。外部キー制約を利用することで、PHPスクリプト側の負担を軽減することができます。
 | 
ON UPDATE | 
ON DELETE | 
| RESTRICT | 
エラー | 
エラー | 
| CASCADE | 
親側の同じ値で子側を更新 | 
親側の削除と同時に子側も削除 | 
| SET NULL | 
子側をNULL値で更新 | 
子側をNULL値で更新 | 
| 外部キー制約の設定 | 
 このように外部制約キーを活用することにより、複数のテーブルにまたがる値を関連付けていくことができます。
さて次回は、これまで作成した「簡易在庫管理システム」と、DTO、DAOパターンを使って修正した「オンラインストア」を統合させ、ショッピングストアの処理を完成させます。