野菜┬根菜類
  │ ├ダイコン
  │ └にんじん
  └果菜類
    └トマト

このような階層構造をデータとして持つ場合
もっとも一般的な方法は親となるデータのIDを持つことと思われます。

カテゴリテーブル
------------------------
ID  | 名前   | 親ID
------------------------
101 | 野菜   | 0  
102 | 根菜類  | 101
103 | 果菜類  | 101
104 | ダイコン | 102
105 | にんじん | 102
106 | トマト  | 103
------------------------

これは「隣接リスト」と呼ばれるやり方で、SQLアンチパターンでは
「アンチパターン」の一つとしています。

その理由として
「1.すべての子孫を取得するには、階層の深さを固定化する必要がある」

階層構造はその性質上、深さに制限がない場合が多いため
階層の深さにかかわらず子孫を取得する必要があります。
ですが、隣接リストでは自身の親IDしか持っていないため
下記のようなクエリでしか、遠い親や子の情報を取得できません。

SELECT c1.名前, c2.名前, c3.名前
 FROM  カテゴリテーブル c1  -- 1階層目
 LEFT OUTER JOIN カテゴリテーブル c2  -- 2階層目
   ON c2.親ID = c1.ID 
 LEFT OUTER JOIN カテゴリテーブル c3  -- 3階層目
   ON c3.親ID = c2.ID 

これはJOINの数を固定しなくてはならないことを表しています。
階層が深くなれば、SQLも修正する必要があります。

野菜┬根菜類
  │ ├ダイコン
  │ │ └練馬大根
  │ └にんじん
  └果菜類
    └トマト

------------------------
ID  | 名前   | 親ID
------------------------
101 | 野菜   | 0  
102 | 根菜類  | 101
103 | 果菜類  | 101
104 | ダイコン | 102
105 | にんじん | 102
106 | トマト  | 103
107 | 練馬大根 | 104
------------------------

SELECT c1.名前, c2.名前, c3.名前, c4.名前
 FROM  カテゴリテーブル c1  -- 1階層目
 LEFT OUTER JOIN カテゴリテーブル c2  -- 2階層目
   ON c2.親ID = c1.ID 
 LEFT OUTER JOIN カテゴリテーブル c3  -- 3階層目
   ON c3.親ID = c2.ID 
 LEFT OUTER JOIN カテゴリテーブル c4  -- 4階層目
   ON c4.親ID = c3.ID 


「2.メンテナンスが面倒」
挿入はかなり楽です。
INSERT INTO カテゴリテーブル VALUES (108,亀戸大根,104);

また、ノード間の移動も簡単に行えます。
UPDATE カテゴリテーブル SET 親ID = 103 WHERE ID = 105; -- にんじんを果菜類に移動

しかし、ノードの削除は少々手間です。
例えば「根菜類」を削除したい場合

SELECT ID FROM カテゴリテーブル WHERE 親ID = 102; -- 104,105が返る
SELECT ID FROM カテゴリテーブル WHERE 親ID = 104; -- 107が返る
SELECT ID FROM カテゴリテーブル WHERE 親ID = 105; -- 105を親に持つデータなし
SELECT ID FROM カテゴリテーブル WHERE 親ID = 107; -- 107を親に持つデータなし
DELETE FROM カテゴリテーブル WHERE ID IN (102,104,105,107);

というように「果菜類」の子孫を検索する必要があります。
親IDに外部キー定義で[ON DELETE CASCADE]を指定すれば
102を削除時に、自動的に子孫も削除されますが、ノードの昇格には対応できません。

例.「果菜類」を削除して、その子どもを第2階層に昇格。
SELECT 親ID FROM カテゴリテーブル WHERE ID = 103; -- 101が返る
SELECT ID FROM カテゴリテーブル WHERE 親ID = 103; -- 106が返る
UPDATE カテゴリテーブル SET 親ID = 101 WHERE ID = 106;
DELETE FROM カテゴリテーブル WHERE ID IN (103);

ノード同士の関係を表すものが直近の親しかないため
何度もSELECT文を発行する必要があるわけです。

ただ隣接リストが必ずしも悪いわけではなく、本書では以下のような場合には
「アンチパターン」を用いてもよいとしています。
『アプリケーションで求められているタスクに適している限りは有効です。
隣接リストの長所は直近の親と子を簡単に取得できることです。また、
列の挿入も容易です。階層構造を持つデータに対して必要な操作がこれのみで
ある場合、隣接リストは効果的に機能します。』

また、階層構造をサポートするSQL拡張機能を備えているデータベース製品を
使用している場合もOKとしています。
例えば、再帰クエリをサポートしている「SQL Sever2005」や「Oracle」などです。

ただ、個人的な感想を述べさせてもらうなら、これについては少々疑問に思います。
WITH句を使用した再帰クエリは使用したことがないので何とも言えませんが
ORACLEの「CONNECT BY PRIOR」を使用した再帰クエリは、処理が遅く
最終的に別の方法に置き換えたという経験があるからです。

それでは隣接リスト以外に階層構造を実現するには、どのような方法があるのか。
長くなったので、次回へ続きます。