SQLアンチパターン - ナイーブツリー

16,606 views

Published on

社内勉強会資料

追記: 2013-10-31
ついったで指摘( https://twitter.com/akuraru/status/395822183777202176 )を受けたので入れ子集合のノード追加の説明の所を修正しました。

Published in: Technology
0 Comments
56 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
16,606
On SlideShare
0
From Embeds
0
Number of Embeds
589
Actions
Shares
0
Downloads
72
Comments
0
Likes
56
Embeds 0
No embeds

No notes for slide

SQLアンチパターン - ナイーブツリー

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

×