Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Introduction of SQL Anti-pattern at Phpcon Hokkaido

546 views

Published on

This slide introduce SQL Anti-pattern for PHP conference Hokkaido.

Published in: Data & Analytics
  • Be the first to comment

Introduction of SQL Anti-pattern at Phpcon Hokkaido

  1. 1. 必読名著
 SQLアンチパターンの紹介 + とあるOSSの闇 PHPカンファレンス北海道2019 株式会社ゆめみ 川井健太 !1
  2. 2. プロフィール • 川井健太(Qiita ID = Mister_K) • 株式会社ゆめみ サーバーサイドエンジニア • Laravel, AWS, Serverless, 木材 とかで開発してます • 趣味は沢山。バイクに乗ります。 !2
  3. 3. この発表は…… • QiitaでちょっとバズったSQLアンチパターンの解説です • どちらかというとDB設計初学者向けです • 名著SQLアンチパターンを例を用いて一部解説します • 実例を用いて設計の大切さを伝えます !3
  4. 4. !4
  5. 5. 発表概要 • SQLアンチパターンとは? • 見るに耐えないER図の紹介 • アンチパターンの紹介 • アンチパターンを回避しよう • とあるOSSに潜むアンチパターン • 究極のアンチパターンとは !5
  6. 6. SQLアンチパターンとは? 設計者が良かれと思って施した設計が 裏目に出て酷い目に合うパターン !6
  7. 7. !7
  8. 8. とある債務管理システム ざっくりとした要件として 1. 債務者を登録でき、プロフィールを入力できる。 2. 債務者の家族構成を登録でき、そのプロフィールを入力できる。 3. 連帯保証人を登録でき、そのプロフィールを入力できる。 4. 取り立て人が債務者に借金の回収に行って、それを記録できる 5. 債務者の借金を管理できる。 6. 債務者の返済状況を管理できる。 7. 債務者、家族、連帯保証人をブラックリストで管理できる。 !8
  9. 9. !9 とある 空想上の 債務者 管理 システム
  10. 10. ジェイウォーク(信号無視) 目的:複数の値を持つ属性を格納する !10 カンマ区切りのリストを格納する
  11. 11. !11 とある 空想上の 債務者 管理 システム
  12. 12. やってしまった事 • debtors.families に families.id を
 カンマ区切りで入れて関連させている • これにより債務者に複数の家族を設定できるようにした !12 id … families 1 … 1,2,3 2 … 4,5 3 … 6,7 debtorsテーブルのレコード
  13. 13. どうなるのか? • 家族を追加するたびに debtors のレコードを更新する必 要が出てしまう • プログラムのバグにより、重複が発生する可能性がある • JOIN句が複雑になる(正規表現が必要&インデックス無効) !13
  14. 14. Entity Attribute Value 目的:可変属性をサポートする !14 Key:Valueの汎用的な属性テーブルを使用する
  15. 15. !15 とある 空想上の 債務者 管理 システム
  16. 16. やってしまった事 • debtor の属性を汎用的にするため、profiles に Key:Valueで値をストアするようにした !16 id key Value 1 is_dead 0 2 middle_name John
  17. 17. どうなるのか? • SQLで用意したデータ制約が利用できなくなる
 よって値の整合性はコードで保障せねばならない • 例えばNOT NULL制約も使えない !17
  18. 18. ナイーブツリー 目的:階層構造を格納し、クエリを実行する !18 常に親のみに依存する
  19. 19. !19 とある 空想上の 債務者 管理 システム
  20. 20. やってしまった事 • 連帯保証人を定義するため debtors.joint_garantor_id を定義した • これに debtors.id を入れる事で、債務者と連帯保証人の 関係を定義できるようにした !20
  21. 21. どうなるのか • RDBMSによっては、
 クエリの長さが拾う階層分だけ長くなってしまう !21 SELECT d1.* d2.*, d3.* FROM debtors c1 LEFT OUTER JOIN debtors c2 ON c2.joint_garantor_id = c1.joint_garantor_id LEFT OUTER JOIN debtors c3 ON c3.joint_garantor_id = c2.joint_garantor_id 3階層まで拾う場合のクエリ
  22. 22. どうなるのか?! • RDBMSによってはメンテナンスが大変
 外部キー制約がある場合は簡単にノードを削除できない !22 Debtor Id = 1 Debtor Id = 2 parent = 1 外部キー制約があると削除できないし、 ノードの昇格も難しい
  23. 23. アンチパターンの例外 • 共通テーブル式を用いた再帰クエリの使えるRDBMSであ れば問題ない • 例として • MySQL 8.0以上 • PostgreSQL 8.4以上 !23
  24. 24. IDリクワイアド 目的:主キーの規約を確立する !24 全てのテーブルに id 列を用いる
  25. 25. !25 とある 空想上の 債務者 管理 システム
  26. 26. やってしまった事 • 全てのテーブルに id カラムを用意した !26
  27. 27. どうなるのか • 冗長となる場合がある • 重複行を許可してしまう場合がある • キーの意味がわからなくなる
 例えばJOIN したらどっちの id だかわからない !27
  28. 28. ポリモーフィック関連 目的:複数の親テーブルを参照する !28 二重目的の外部キーを使用する
  29. 29. !29 とある 空想上の 債務者 管理 システム
  30. 30. やってしまった事 • ブラックリストで債務者とその家族を管理したい • だから blacklist.debtor_family_id には debtors.id, families.idのいずれかが入るようにした !30 Id debtor_family_id 1 24(debtors の id) 2 24(families の id)
  31. 31. どうなるのか • 外部キー制約、つまり参照整合性制約を定義できない • 誤って参照先の存在しないブラックリストのレコードがで きてしまうかも。 • 外部キー制約なんて邪魔?
 それこそアンチパターンの一つ! !31
  32. 32. アンチパターンを回避しよう !32
  33. 33. !33 リファクタリング後のER図
  34. 34. ポリモーフィック関連回避 • 共通の親テーブルを作成する
 例:debtor の親クラスを作り、それをブラックリスト に追加する。 !34 債務者 • 人物id 人物
  35. 35. !35 今回は人物エンティティ に統一した 連帯保証人は契約単位で 設定されるので、別にテー ブルを用意して人物を関 連づけた
  36. 36. ナイーブツリー回避 • 共通テーブル式を指定する再起クエリを利用できる RDBMSを用いる • 代替ツリーモデルを使用する
 経路列挙 ディレクトリ構造を記録するカラムを追加する。
 入れ子集合 子孫の集合に関する情報を各ノードに格納する。
 閉包テーブル エンティティの親子関係を定義する別のテーブルを用意。
 詳しいことは本を見てくれ! !36
  37. 37. ジェイウォーク回避 • 交差(関連、中間)テーブルを作成する !37 familie Id = 2 debtor Id = 3 closs_table debtor_id = 3 familie_id = 2
  38. 38. !38 家族関係の交差 テーブルを作成した。 同時にナイーブツリーの 回避も行なった。
  39. 39. EAV回避 • 要件を絞る
 とりあえず拡張できるようにするには犠牲を伴うため • サブタイプのモデリングを行う • JSON型を用いる !39
  40. 40. !40 債務者 • Id • Name • Address 低評価債務者 • has_bunkrupt 特別債務者 • is_richmen 要件を確定し、
 債務者を3パターンに分ける SELECT するときはサブテー ブルを JOIN して用いる やはり要件は絞る必要がある サブタイプモデリング
  41. 41. !41 要件を整理して 項目を絞った
  42. 42. idリクワイアド回避 • 複合ユニークキーを用いる • テーブル名_id と言うように名前をつける !42
  43. 43. !43 person_to, person_from 複合主キーを定義
  44. 44. ここからは PHPに関係するお話 !44
  45. 45. この世には 多くの人に使われる 闇を抱えしOSSが存在する !45
  46. 46. !46
  47. 47. WordPressとは • オープンソースのブログソフトウェア
 ウェブサイトのCMSとしても使われる • SEOに最適化されている点でとても強い
 34%のウェブサイトがWordPress製らしい(公式より) • 2003年に最初のバージョンが世に出た !47
  48. 48. !48 WordPressの ER図 (v4.9時点)
  49. 49. !49 EAVが使われている!
  50. 50. 何故EAVを使ったのか考察 • 最初にWordPressを作った人が、とりあえずEAVを使っ た。 • 2003年当時、json型はなかった。 • SQLアンチパターンの初版が2010年なので、認知もさ れていなかっただろう。 !50
  51. 51. 何故EAVを使ったのか考察 • WordPressがウケたが、既にデータはストアされていて 手を出せなくなっていた。 • 自社でソース・データベースを管理していたら改善はでき ただろうが、WordPressは世界中で動きすぎた。 • スキーマをメタテーブルで管理する地獄もある !51
  52. 52. WordPressから学べる事 • とりあえず世に出すことは大切 • でもデータベースはちょっと考えろ!
 ウケた時どうなっても知らんぞ! • 究極、ソースは後からどうにでもなる
 データベースは容易に変更できない !52
  53. 53. 究極のアンチパターンとは 将来的な変更や アンチパターンを意識せず 適当に設計をすること !53
  54. 54. !54 https://www.satofull.jp/static/oenkifu/201909_typhoon_15.php さとふる にて募金を受け付けています ご静聴ありがとうございました

×