Closed21

DB設計を学びなおす

r-sugir-sugi

https://en-ambi.com/itcontents/entry/2018/12/11/110000/#仕様追加に強い設計

https://www.wantedly.com/companies/lapras/post_articles/349385

https://soudai.hatenablog.com/entry/2018/05/01/204442#google_vignette

https://agilejourney.uzabase.com/entry/2022/07/28/103000

拡張と破綻
会員に関していえば、会員の種別だけでも複数存在するケースもあるでしょう。たとえば一時会員、プレミアム会員、ゲストといった種別(type)が考えられます。さらに、種別それぞれに退会、休会、未払い、仮会員などの状態(status)に関する情報も付加されることもあり、種別、状態の多種多様な組み合わせを表現する必要があります。
これらを表現するためには、memberテーブルに種別を表すtypeカラムと、状態を表すstatusカラムを追加するといった実装が考えられるかもしれません。しかし、データベースには「プレミアム会員が『未払いstatus』の場合、通常会員として扱う」といったビジネスロジックが注入されることが往々にしてあるでしょう。さまざまな要件を詰め込みすぎてしまうと、適切なデータを取り出すためのSQLは複雑になり、 今いる通常会員を数える といった、本来簡単なはずの分析SQLも非常に難解なものになってしまいます。

ここでもusersテーブルと同様に小さくテーブルを追加していくことで対応できます。今回のサンプルでも小さくテーブルを分けたほうがアプリケーションとしてメリットがあります。「blog」「掲示板」「Q&A」がそれぞれ同様のテーブル構造だったとしても、「小さなテーブル」として都度設ける形で実装した場合は、それぞれのテーブルからデータを取り出すことになるので、素直なSQLになります。この場合はORMの恩恵を受けながらシンプルに実装することができます。プログラムと同様に一見同じような構造に見えても、意図的に共通化を避けることで“シンプルさ”を実現できる場合があるのは、テーブル設計についても同様なのです。

そして最後にINDEXの数にも注目しましょう。主キーは必ずありますが、外部キー制約とユニーク制約を除いたINDEXは主に検索のために必要なINDEXです。検索のWHEREの対象の数だけそのテーブルの責務が大きいといえ、4つ以上のINDEXが必要な場合も同じく深く考察する必要があります。隠れた状態をWHEREで絞り込んでいたり、種別をWHEREで絞り込んでいるケースが見えてくることがあります。

このようにシンプルな設計を目指して考察を繰り返していくことが重要です。そして同じくらい重要なこととして認識すべきはイージーとシンプルは両立できる、ということです。シンプルを目指し考察を繰り返すことがまさにデータモデリングであり、変化に強い設計につながっていくのです。

ここまでシンプルな実装を目指しましょうと強調してきましたが、「シンプルな実装」とはなんでしょうか。RDBMSを使う上でシンプルな実装のヒントは正規化です。正規化のコツは次のように表現できます。

事実だけを保存する
重複がない
不整合がない
nullがない

外部キー制約と共有ロック
https://speakerdeck.com/andpad/mysqlfalserotukunituite-ji-ben-bian-d367fac2-7653-4e19-b811-2c326703d326?slide=22

https://www.slideshare.net/ichirin2501/ss-44642631

r-sugir-sugi

https://www.slideshare.net/slideshow/ss-250716400/250716400

https://scrapbox.io/kawasima/イミュータブルデータモデル

CRUDのうちUPDATEがもっともシステムを複雑化する。更新には複雑なルールが伴うからだ。業務的に複雑なルールが存在するのは仕方ないこともあるが、システム、設計で複雑さを更に増さないようにしたい。UPDATEに着目し、その発生をできるだけ削ることによって複雑さをおさえるためには、まずデータモデルをそのように設計しておかなけれなならない。このイミュータブルデータモデルは、それを手助けする手法で、手順に沿って実施すればある程度のスキルのバラつきも吸収できるように組み立てられている。

https://www.slideshare.net/slideshow/ronsakucasual/52256922

https://www.slideshare.net/slideshow/ss-69664821/69664821

r-sugir-sugi

制約 Must

テーブルの色分けする(Event, Resource)
レコードはInsertのみ(イミュータブルテーブル)
Event系のテーブルにのみcreated_atのみをつけてよい(Resourceはタイムスタンプを持たない)
※タイムスタンプは、Eventテーブルに1つまで許容する。

NOT NULL制約 (オプショナルな属性値を入れてnull許容しないこと)
参照されない不要なIDをつけないこと

テーブルを切り分ける

同じ名前でも、スコープが異なる場合は別テーブル
データ量が多い、常に必要じゃない値が入っている場合別テーブル
検索対象用に別テーブルを作ってもよい
履歴を残したい場合は、子テーブルに対する履歴テーブルを作る
indexを4つ以上はれる場合
交差テーブルにすべき場合(独立して存在する時、関係性を表現する時)

実装上の方針

joinが辛い

マテリアル or viewで対応する
repository層でfetchするときに、中間層であらかじめjoin済みのレコードを返す
などが考えられる

運用上の方針

不要なレコード

バッチ処理で不要なレコードを削除する

カラムのdrop

appのみのPR: 参照しないようにする
migrationPR: カラムをremoveする

カラムの追加

migrationのみのPR: カラムを追加する
appのみのPR: 参照する

※Not Nullカラムの場合、追加時はnull許容、その後に初期値を入れてnot nullに変更する?

フォーム内の任意項目

nullableな値はカラムに入れないとしたら、専用のカラムを用意するのか?
まさかjson型を使う、、?

このスクラップは2ヶ月前にクローズされました