カテゴリーとか階層構造を持ったデータをMySQLで扱いたい。
どうすりゃいいのさ?
まずは、Google検索!
MySQL 階層構造 - Google検索
いろいろヒットしました!
リレーショナルデータベースの機能内で階層データを扱う方法は以下の3モデルがあります。
- 隣接リストモデル [Adjacency List Model]
- 入れ子集合モデル [Nested Set Model]
- 経路列挙モデル [Path Emuneration Model](もしくは、経路実体化モデル [Materialized Path Model])
それぞれのノード(階層データを構成する個々の要素)が親を保存しておきツリー構造をたどることによりデータ構造を表現します。
Oracleの階層問い合わせのようにSQL拡張がない場合(MySQLにはありません)は、別途プログラム等でループを組む必要がある場合があり、パフォーマンスが良くありません。
比較的理解しやすいですがMySQLで利用するには用途が限られます。
ノードを円と見なし親子関係を左右の番号を使用し円の包含関係として捉える事によりデータ構造を表現します。
検索に関しては隣接リストモデルと違い圧倒的有利であるが、直接の親・子やツリー構造をたどるのは複雑になります。
また、親子関係を左右の番号が追加、削除を繰り返すことにより歯抜けになります。(歯抜けが気持ち悪ければ正規化が必要)
構造は面白いが理解しづらくパフォーマンスが良くないところが見られますが、MySQLでも利用できます。
各ノードに絶対パスをデータとして保存しデータ構造(UNIXのファイルシステムやURLの構造にそっくり)を表現します。
ノード自身のレコードに親子関係がパスとして含まれているので、SQL文が簡素になります。
また、パスは一意なりますし、MySQLでも正規表現を扱えるようになりましたので、極めて高い親和性と高いパフォーマンスが望めます。
MySQLで利用するには、この経路列挙モデルが一番親和性が高いように思います。
経路列挙モデルでOK
位置情報をファイルシステムと同じ方法で持つ。直観的に分かりやすいので良さげ。
経路列挙モデルで、同じ階層のデータ(同じ親ノードにぶら下がっている子ノード同士)を並び替えるにはどうしたら良いか?
sortフラグを持たせて、大小の数値を付与すればいいの?
並び順まで考えたら、入れ子モデルの方が良いだろうか?
経路列挙モデルは、階層構造の上下関係のデータを持っているけど、並び順のデータは表現できない。
並び順を指定するには、入れ子モデルの方が簡潔だろうか?
…ということで入れ子モデルに変更w
入れ子モデルには、
SQLで木と階層構造のデータを扱う(3)――入れ子区間モデル
入れ子集合モデルには、整数を座標に使っているため、更新時のパフォーマンスが悪いという欠点がありました。それを克服するため、整数から有理数へ一般化したモデル。
第6回 SQLで木構造を扱う~入れ子区間モデル (1)もしも無限の資源があったなら
http://gihyo.jp/dev/serial/01/sql_academy2/000601
データの追加を繰り返すと、識別子の少数化がどんどん進むので、整数を振り直す(正規化)する処理を追加してみるとか。
=[lft][rgt]を整数に直す処理
ここまで考えて、経路列挙モデルでsortするアイデアを思いついてしまった!!!
これはSQLだと複雑になりそうなので、プログラム側で処理させる。
カラム名 | データ型 | 内容 |
id | int | カテゴリーID |
name | text | カテゴリー名 |
path | text | ノードのパス情報 |
sort | float | 並び順 |
sort_normal | int | 並び順を正規化するための一時的な作業用カラム。無くてもOK? |