sqlアンチパターン - ナイーブツリー
DESCRIPTION
社内勉強会資料 追記: 2013-10-31 ついったで指摘( https://twitter.com/akuraru/status/395822183777202176 )を受けたので入れ子集合のノード追加の説明の所を修正しました。TRANSCRIPT
![Page 1: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/1.jpg)
ナイーブツリー
SQLアンチパターンその2 素 朴 な 木
![Page 2: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/2.jpg)
ナイーブツリー is
直近の親のみを参照する事でツリー構造を表現する SQLアンチパターン
![Page 3: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/3.jpg)
シナリオ
A「ブログのコメント欄さ」
B「はい」
A「よく議論やってるじゃん?」
B「ですね」
A「スレッド形式で表示できたら分かりやすいと思うんよね」
B「考えてみます」
![Page 4: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/4.jpg)
シナリオ
B「今のテーブル定義こんな感じだから」
id comment
1 たけのこ派駆逐したい
2 きのこ派怖いわー
3 >>1 同意
4 >>2 たけのこ汚い
たけのこ派駆逐したい
きのこ派怖いわー
>>1 同意
>>2 たけのこ汚い
![Page 5: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/5.jpg)
シナリオ
B「コメント間に親子関係があればいいわけで」
id comment
1 たけのこ派駆逐したい
2 きのこ派怖いわー
3 >>1 同意
4 >>2 たけのこ汚い
たけのこ派駆逐したい
きのこ派怖いわー
>>1 同意
>>2 たけのこ汚い
![Page 6: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/6.jpg)
シナリオ
B「こうすればいいか」
id parent comment
1 null たけのこ派駆逐したい
2 1 きのこ派怖いわー
3 1 >>1 同意
4 2 >>2 たけのこ汚い
たけのこ派駆逐したい
きのこ派怖いわー
>>1 同意
>>2 たけのこ汚い
![Page 7: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/7.jpg)
シナリオ
B「こうすればいいか」
id parent comment
1 null たけのこ派駆逐したい
2 1 きのこ派怖いわー
3 1 >>1 同意
4 2 >>2 たけのこ汚い
たけのこ派駆逐したい
きのこ派怖いわー
>>1 同意
>>2 たけのこ汚い
素 朴 な 木
ナイーブツリー
![Page 8: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/8.jpg)
Bの主張
葉の追加も簡単だし参照整合性も維持できるし最強に見える。
![Page 9: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/9.jpg)
Bの主張
葉の追加も簡単だし参照整合性も維持できるし最強に見える。
ツリーの参照は?
コメント数の数えるとか集約系の関数は?
非葉ノードの削除は?サブツリーの昇格は?
![Page 10: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/10.jpg)
ナイーブツリー選択のデメリット
(サブ)ツリーを引っこ抜けない
ツリー全体
あるいはあるコメントのサブツリー
![Page 11: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/11.jpg)
ナイーブツリー選択のデメリット
(サブ)ツリーを引っこ抜けない
ツリー全体
あるいはあるコメントのサブツリー
たけのこ派駆逐したい select
c1.* from comm c1
![Page 12: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/12.jpg)
ナイーブツリー選択のデメリット
(サブ)ツリーを引っこ抜けない
ツリー全体
あるいはあるコメントのサブツリー
たけのこ派駆逐したい
きのこ派怖いわー
select c1.*, c2.* from comm c1 left outer join comm c2 on c1.id = c2.parent コメント1を親に持つコメント
![Page 13: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/13.jpg)
ナイーブツリー選択のデメリット
(サブ)ツリーを引っこ抜けない
ツリー全体
あるいはあるコメントのサブツリー
たけのこ派駆逐したい
きのこ派怖いわー
>>2 たけのこ汚い
select c1.*, c2.*, c3.* from comm c1 left outer join comm c2 on c1.id = c2.parent left outer join comm c3 on c2.id = c3.parent コメント1を親に持つコメント
を親に持つコメント
![Page 14: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/14.jpg)
ナイーブツリー選択のデメリット
(サブ)ツリーを引っこ抜けない
ツリー全体
あるいはあるコメントのサブツリー
n階層取得したい場合は…?
子が存在し続ける限り自動でjoinし続けてくれるSQLは書けない
取得できる階層が固定される
必然的にある任意の(サブ)ツリーのコメント総数みたいな集約関数も使えない
![Page 15: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/15.jpg)
ナイーブツリー選択のデメリット
非葉ノードとそのサブツリーの削除
コメント1削除
コメント1を親に持つコメントを削除
…とやりたいけど参照整合性のため子から削除する必要がある
c1、c1を親に持つ(c2)、c2を親に持つ…を結果が返らなくなるまで繰り返して削除対象のコメントを取得し、子側からdeleteしないとダメ
とは言えdelete on cascadeが使えれば、これは自動化出来る
![Page 16: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/16.jpg)
ナイーブツリー選択のデメリット
非葉ノードとそのサブツリーの削除
たけのこ派駆逐したい select
![Page 17: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/17.jpg)
ナイーブツリー選択のデメリット
非葉ノードとそのサブツリーの削除
たけのこ派駆逐したい
きのこ派怖いわー
select
select
![Page 18: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/18.jpg)
ナイーブツリー選択のデメリット
非葉ノードとそのサブツリーの削除
たけのこ派駆逐したい
きのこ派怖いわー
>>2 たけのこ汚い
select
select
select
![Page 19: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/19.jpg)
ナイーブツリー選択のデメリット
非葉ノードとそのサブツリーの削除
たけのこ派駆逐したい
きのこ派怖いわー
select
select
delete
![Page 20: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/20.jpg)
ナイーブツリー選択のデメリット
非葉ノードとそのサブツリーの削除
たけのこ派駆逐したい select
delete
![Page 21: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/21.jpg)
ナイーブツリー選択のデメリット
非葉ノードとそのサブツリーの削除
delete
![Page 22: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/22.jpg)
ナイーブツリー選択のデメリット
非葉ノード削除とそのサブツリーの昇格
非葉ノードにくっついていたサブツリーを自分の親の子へ昇格させる場合
こちらはdelete on cascadeズパーンとはいかない
削除前に自分の子を取得し、子の親を自分の親へ付け替えた上で削除する必要がある
![Page 23: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/23.jpg)
ナイーブツリー選択のデメリット
非葉ノード削除とそのサブツリーの昇格
たけのこ派駆逐したい
きのこ派怖いわー
>>1 同意
>>2 たけのこ汚い
←削除したい
![Page 24: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/24.jpg)
ナイーブツリー選択のデメリット
非葉ノード削除とそのサブツリーの昇格
たけのこ派駆逐したい
きのこ派怖いわー
>>1 同意
>>2 たけのこ汚い
←削除したい
←子の親を
![Page 25: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/25.jpg)
ナイーブツリー選択のデメリット
非葉ノード削除とそのサブツリーの昇格
たけのこ派駆逐したい
きのこ派怖いわー
>>1 同意
>>2 たけのこ汚い
←削除したい
←自分の親へ更新
![Page 26: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/26.jpg)
ナイーブツリー選択のデメリット
非葉ノード削除とそのサブツリーの昇格
たけのこ派駆逐したい
>>1 同意
>>2 たけのこ汚い
←削除
![Page 27: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/27.jpg)
ナイーブツリー選択のデメリット
非葉ノード削除とそのサブツリーの昇格
たけのこ派駆逐したい
>>1 同意
>>2 たけのこ汚い
![Page 28: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/28.jpg)
ナイーブツリー選択のデメリット
まとめ
再帰的にクエリ吐けるとかじゃなきゃやってられない
サブツリー一括の削除や移動はまだしもノードの昇格とかちょっと凝った操作を実現するのはつらい。
![Page 29: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/29.jpg)
B「僕は…どうしたらよかったんだ…」
![Page 30: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/30.jpg)
解決策
代替ツリーモデルを使いましょう
ツリーを表現する他の方法
経路列挙モデル
入れ子集合モデル
閉包テーブルモデル
![Page 31: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/31.jpg)
解決策 : 経路列挙モデル
id path comment
1 1/ たけのこ派駆逐したい
2 1/2 きのこ派怖いわー
3 1/3 >>1 同意
4 1/2/4 >>2 たけのこ汚い
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
![Page 32: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/32.jpg)
解決策 : 経路列挙モデル
メリット サブツリーの一括取得ができる
likeを用いたパターンマッチ path like ‘1/2/%’
ノードの追加は親ノードのpathにidを連結したpath文字列で追加する
葉ノードの削除はdeleteするだけ
デメリット >突然のジェイウォーク!< 長さ制限とか参照整合性とか同様の脆弱性 非葉ノードの削除、サブツリーの昇格とか移動とかは子のpathを全て更新しないといけないので隣接ツリーよりだるい
![Page 33: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/33.jpg)
解決策 : 経路列挙モデル
id path comment
1 1/ たけのこ派駆逐したい
2 1/2 きのこ派怖いわー
3 1/3 >>1 同意
4 1/2/4 >>2 たけのこ汚い
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
2に子を追加
![Page 34: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/34.jpg)
解決策 : 経路列挙モデル
id path comment
1 1/ たけのこ派駆逐したい
2 1/2 きのこ派怖いわー
3 1/3 >>1 同意
4 1/2/4 >>2 たけのこ汚い
5 1/2/5 きのこfudやめろ たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い きのこfudやめろ
![Page 35: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/35.jpg)
解決策 : 経路列挙モデル
id path comment
1 1/ たけのこ派駆逐したい
2 1/2 きのこ派怖いわー
3 1/3 >>1 同意
4 1/2/4 >>2 たけのこ汚い
5 1/2/5 きのこfudやめろ たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い きのこfudやめろ
path like ‘1/2%’で サブツリーを選択できる。
![Page 36: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/36.jpg)
解決策 : 入れ子集合モデル
id left right comment
1 1 8 たけのこ派駆逐したい
2 2 5 きのこ派怖いわー
3 6 7 >>1 同意
4 3 4 >>2 たけのこ汚い
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
6
4
8
7 5
![Page 37: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/37.jpg)
解決策 : 経路列挙モデル
Left? Right? Left : その子ノードの持つどのLR値よりも小さい値
Right: その子ノードの持つどのLR値よりも大きな値
深さ優先探索で求まる
メリット 子孫、先祖ノードの一括取得ができる
ノードを削除すると自動的に子ノードが親ノードの子へ昇格する
デメリット ノードの追加や更新(移動)が複雑過ぎる
LR値の再計算が必要
隣接リストでは簡単な直近の子の取得がだるい
![Page 38: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/38.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
![Page 39: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/39.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
![Page 40: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/40.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
![Page 41: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/41.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3 4
![Page 42: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/42.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3 4
5
![Page 43: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/43.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
6
4
5
![Page 44: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/44.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
6
4
7 5
![Page 45: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/45.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
6
4
8
7 5
![Page 46: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/46.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
6
4
8
7 5
サブツリーの どのLR値より小さい
![Page 47: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/47.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
6
4
8
7 5
サブツリーの どのLR値より大きい
![Page 48: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/48.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
6
4
8
7 5
1の子孫ツリーの取得 →L値が1~8のコメント
![Page 49: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/49.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
6
4
8
7 5
1の子孫ツリーの取得 →L値が1~8のコメント
![Page 50: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/50.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
6
4
8
7 5
4の先祖ツリーの取得 →L <= 3 <= R を満たすコメント
![Page 51: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/51.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
6
4
8
7 5
4の先祖ツリーの取得 →L <= 3 <= R を満たすコメント
![Page 52: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/52.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
6
4
8
7 5
2にノードを追加
![Page 53: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/53.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
6
4
8
7 5
2にノードを追加
きのこfudやめろ
5 6
↑ L:R = 5:6を追加したい
さっきの深さ優先探索より 2:5の子に追加するのでこのノードのLR値は5:6になるはず (親のノードのR値を追加しようとするノードのL値にする)
![Page 54: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/54.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
6
4
8
7 5
2にノードを追加
きのこfudやめろ
5 6
↑ 追加されたノード以降のLR値を再計算(+2する)
![Page 55: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/55.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
8
4
10
9 7
2にノードを追加
きのこfudやめろ
5 6
![Page 56: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/56.jpg)
解決策 : 入れ子集合モデル
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
1
2
3
8
4
10
9 7
2にノードを追加
きのこfudやめろ
5 6
↑ 追加
![Page 57: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/57.jpg)
解決策 : 閉包テーブルモデル
id comment
1 たけのこ派駆逐したい
2 きのこ派怖いわー
3 >>1 同意
4 >>2 たけのこ汚い
たけのこ駆逐したい
>>1 同意 きのこ派怖いわー
>>2 たけのこ汚い
先祖 子孫
1 1
1 2
1 4
1 3
2 2
2 4
3 3
4 4
![Page 58: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/58.jpg)
解決策 : 閉包テーブルモデル
ノード同士の関係性を定義するテーブルを追加する 直近の親子関係だけじゃなく、全体のパスを持つ
離れたノードの親子関係も 自分自身を参照するパスも含める
メリット ツリーの取得、移動、ツリーの削除、ツリーの昇格、マルチルート、複数の親への所属などとにかく柔軟
入れ子集合ほどは複雑じゃない
デメリット 柔軟過ぎる 入れ子ほどじゃないと言うだけでそこそこ複雑 パステーブルのデータ量が激ふえする
![Page 59: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/59.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
A
C B
D
先祖 子孫
![Page 60: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/60.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
A
C B
D
先祖 子孫
A A
![Page 61: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/61.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
A
C B
D
先祖 子孫
A A
A B
![Page 62: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/62.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
A
C B
D
先祖 子孫
A A
A B
A C
![Page 63: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/63.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
A
C B
D
先祖 子孫
A A
A B
A C
A D
![Page 64: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/64.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
A
C B
D
先祖 子孫
A A
A B
A C
A D
B B
![Page 65: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/65.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
A
C B
D
先祖 子孫
A A
A B
A C
A D
B B
B D
![Page 66: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/66.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
A
C B
D
先祖 子孫
A A
A B
A C
A D
B B
B D
C C
![Page 67: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/67.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
A
C B
D
先祖 子孫
A A
A B
A C
A D
B B
B D
C C
D D
![Page 68: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/68.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
A
C B
D
先祖 子孫
A A
A B
A C
A D
B B
B D
C C
D D 直近だけでなく全てのノードの親子関係を定義 パステーブルの1行は上図の矢印1本に相当する
![Page 69: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/69.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
A
C B
D
先祖 子孫
A A
A B
A C
A D
B B
B D
C C
D D Aの子孫ツリー →Aを先祖として持つ子孫
![Page 70: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/70.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
A
C B
D
先祖 子孫
A A
A B
A C
A D
B B
B D
C C
D D Dの先祖ツリー →Dを子孫として持つ先祖
![Page 71: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/71.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
E
A
C B
D
先祖 子孫
A A
A B
A C
A D
B B
B D
C C
D D Bの子としてEを追加 →自己参照追加 →Bを子孫として持つ先祖の子孫にEを追加
E
E E
A E
B E
![Page 72: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/72.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
E
A
C B
D
先祖 子孫
A A
A B
A C
A D
A E
B B
B D
B E
C C
D D
E E Bの子としてEを追加 →自己参照追加 →Bを子孫として持つ先祖の子孫にEを追加
E
![Page 73: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/73.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
E
A
C B
D
先祖 子孫
A A
A B
A C
A D
A E
B B
B D
B E
C C
D D
E E Bのツリーをまるごと削除 →Bを子孫とする関係を削除
E
![Page 74: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/74.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
E
A
C B
D
先祖 子孫
A A
A B
A C
A D
A E
B B
B D
B E
C C
D D
E E Bのツリーをまるごと削除 →Bを子孫とする関係を削除 →Bの子孫を子孫とする関係を削除 (Bを先祖とするノードを子孫として持つ関係)
E
![Page 75: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/75.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
E
A
C B
D
先祖 子孫
A A
A C
C C
Bのツリーをまるごと削除 →Bを子孫とする関係を削除 →Bの子孫を子孫とする関係を削除 (Bを先祖とするノードを子孫として持つ関係)
E
![Page 76: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/76.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
E
Bを削除 →Bを子孫とする関係を削除
先祖 子孫
A A
A B
A C
A D
A E
B B
B D
B E
C C
D D
E E
A
C B
D E
![Page 77: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/77.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
E
Bを削除 →Bを子孫とする関係を削除 →Bを先祖とする関係を削除
先祖 子孫
A A
A B
A C
A D
A E
B B
B D
B E
C C
D D
E E
A
C B
D E
![Page 78: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/78.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
E
Bを削除 → C, Eが自動的にAの子に昇格 →Bを子孫とする関係を削除 →Bを先祖とする関係を削除
先祖 子孫
A A
A C
A D
A E
C C
D D
E E
A
C B
D E
![Page 79: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/79.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
E
A
C B
D
先祖 子孫
A A
A B
A C
A D
A E
B B
B D
B E
C C
D D
E E
BをCの子へ移動 →Bを子孫とする関係と Bの先祖からBの子孫への関係を削除 (ただし自己参照を除く)
E
![Page 80: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/80.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
E
A
C B
D
先祖 子孫
A A
A C
B B
B D
B E
C C
D D
E E
BをCの子へ移動 →Bを子孫とする関係と Bの先祖からBの子孫への関係を削除 (ただし自己参照を除く)
E
![Page 81: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/81.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
E
A
C B
D
先祖 子孫
A A
A C
B B
B D
B E
C C
D D
E E
BをCの子へ移動 →Cを子孫とする移動先サブツリーから 孤立中のBのツリーのノードへの全経路を cross joinで生成する
E
A B
A D
A E
C B
C D
C E
![Page 82: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/82.jpg)
解決策 : 閉包テーブルモデル
node
A
B
C
D
E
A
C
B
D
先祖 子孫
A A
A B
A C
A D
A E
B B
B D
B E
C B
C C
C D
C E
BをCの子へ移動 E
D D
E E
![Page 83: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/83.jpg)
まとめ
隣接リスト シンプル ツリー取得、操作が苦手
経路列挙 シンプルかつツリー取得可能 ジェイウォーク
入れ子集合 子孫、先祖ツリーの取得が可能 ノードの削除で子ノードが自動で昇格する ツリー操作が死ぬほど複雑
閉包テーブル 入れ子集合に比べれば(比べればと言う程度)シンプル 先祖、子孫の取得、サブツリーごと削除、子の昇格、ツリー移動な
ど大体の事が出来る その代わりツリー構造管理テーブルが必要 パステーブルのデータ量がめっちゃ増える
![Page 84: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/84.jpg)
ナイーブツリー(素朴な木)
ツリー表現の解決策としては素朴過ぎると言う意味
素朴故に多用される。
ただし常にナイーブツリー is 悪ではない
ツリーを表現する手法は他にもあるんだよ、ということを覚えておきましょう
![Page 85: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/85.jpg)
アンチパターンを用いていい場合
隣接リストでアプリの要件を満たせる場合
実際シンプル
その他の手法(特に入れ子集合)はそれなりの複雑さを持ち込むことになる
複雑さ is バグの元
利用DB製品が再帰クエリ構文をサポートする場合
ぽすぐれ、SQLServer、Oracle、DB2
再帰クエリなら隣接リストでもツリー取得可
![Page 86: SQLアンチパターン - ナイーブツリー](https://reader035.vdocuments.site/reader035/viewer/2022062319/55660402d8b42aa6628b4b1d/html5/thumbnails/86.jpg)
おわり