This page looks best with JavaScript enabled

DB設計したいNight #6 正規化【メモ】

 ·   ·  ☕ 5 分で読めます
✏️

ちょうどいい正規化の話 by @nakunaru

最低限のライン

  • 不整合を起こさない
  • データを失わない

ダブらせない

→更新漏れ、複数行(リソースをたくさん使う)
1 fact 1 place

余談
1 fact q place は結構難しい。情報ってなんだ??

データと情報の違い
データ: 値そのもの。 3520
情報: データを分類・整理して意味を持ちせたもの。商品番号800番の価格3520

「商品の価格」「販売時の価格」などの「〇〇の□□」という見方でデータを見ると情報が浮かび上がる
同じ「価格」でも、「商品価格」と「購入価格(割引があるかも??)」なら共通化してはいけないよね??

ロストしない

DELETEしたときに想定外の情報が消えないようにする

買い物かごから商品を消す→元の商品データが消えるwwwww(笑えねえ)事態にならないために
そのデータを消すことで何が消えるのか??

主キーに仕事させる

受注番号と注文者番号「あれ、注文者番号があれば受注番号がなくても特定できるよ……」だめですねえ
(間接関数従属)

正規化しましょう

つまり、正規化の視点のお話!!

A. 定価・割引率・結果を明細テーブルに履歴として持っておくと、まあデータが無くなることはなくて安全。
事実をぶっこんでおけば即死は免れる。

Q. 1 fact 1 place
A. 昔の職場のエライ人が言ってた。

Q. 正規化ってどこまであるの?
A. 第6くらいまである。第三+ボイスコッド正規化まですればいい。

申込におけるDB設計の失敗とたられば by @ngmt83

推し本

  • らくらくERレッスン
  • SQLアンチパターン
  • そーだい本

10社の口コミと最安値がすぐわかる

  • 入力項目が多い
  • 入力に悩むことが多い
  • 引っ越し一括見積りサイトに似ている

引っ越しサイトを例に挙げる

  1. 概要を入力
  2. 荷物について
  3. 個人情報
  4. (簡単なアンケート)

「どのステップでどの項目を入力するかは変わるかも」

……とりあえず、申し込みテーブルに1.〜4.の全ての情報を突っ込む設計

利 変更を行いやすい
不利 NULL入り放題、正規化ナニソレ、バリデーションは全てアプリで

途中で離脱したユーザーのために個人情報を先に入れよう。んで、途中のユーザーは途中のステップから入力したい。
→ステップという定義が乱れる

アンケートは荷物と一緒でいいな。
→ステップ合併

正規化しなかったのはまあよかったのか……??

「1問1問のフォームを同時並行で試したい」

ステップを各項目で……。

最初に正規化していたら相次ぐ変更で死んでいたな。。
1テーブル全ツッコミはまだマシだった。
だがしかし、リファクタリングする前提で工数確保したいですね、、難しいけど、、

「電話番号があればメールいらないかも」バリデーションんんん??
<= To Be Continued…

これは相当やばいらしい(あまりピンとこなかった)

申し込みのDB設計は変わりやすい!!

  • ユーザーの入り口で重要
  • 繰り返し行うものではないから
  • 初期段階のデータの不整合はよろしくない

データベース設計ミスるとロジックが死ぬ。。

雑談

アンケートも変わりやすいよね。
「〇〇年の結果テーブル」みたいな切り離せるけど、申込みはその後も使うから難しいよね

「RDBにしない選択肢」 jsonもアリ??

条件とか色々出てくるときついよね

ジーエーのデータ??というのがあるらしい(途中離脱の話)これぐぐっても出てこなかったんだが。。
アプリ側の話らしい??
(追記)グーグルアナリティクスっぽい

NULL嫌いのUPDATEしないDB設計 by @sinsoku_listy

nullとは

  • 四則演算できん(すべてをnullにする)
  • 比較できん(すべてをry)null == null もnull
  • 集合関数で無視される
    • 平均とか取ろうとするとないことにされる
  • 並びかえがヤバイ
    • そういえば、nullのせいでランキングの並べ替えできんかったな(Firestoreだけど)
  • 3値論理 true, false, nullというbooleanになりうる

nullの侵入を防ぐ

  • Railsのバリデーションで防ぐ
    • required:true(勝手についてる), presense: true
    • しかしすり抜けうる。バッチ処理とか
  • not null制約(migration)
    • DB側でエラーを出してくれるよ!

具体例

記事の下書き・公開・非公開

公開→公開者・公開日時 published_at(nullを許可)
バリデーション複雑になるし認可が面倒
fat controller updateはデッドロックの危険性がある

テーブルを分ける

article_publications, article_archivesとかで別にする
モデルも分ける(関連するpublicationモデルがあるかどうかで判定する)

コントローラも公開アクションをcreateにできる!

パフォーマンスは悪くなってからstatusを作る

ActiveRecord6.0

  • where.missing(:author)
    • 論理削除に便利
    • gem activerecord-missingも使える
  • check_constraint

Q. 記事の削除はどうするの?
A. KPI的にアーカイブにした方がいい。論理削除

Q. パフォーマンスが悪くなったらryのstatus変更はどうする?
A. article.publicationafter_commitでコールバック
保存した後にarticleのstatusを変える
管理者の許可〜とか、ちょっと遅れてもいいなら非同期処理にする

Q. 削除フラグは別のストレージに入れて消すのはどう?
A. user_archiveテーブルとかにjsonでカラムを残して本物は消す
read_onlyならjsonはアリ

Q. mysql, ポスグレとかjsonにindexは貼れるの? db2は行ける
A. generated column(生成カラム)で貼れる??

Q. 公開→非公開→公開のパターン
A. 公開と非公開はhas_many 非公開を消すことで公開状態にする設計
結局は要件次第
いつからいつまで公開、いつからいつまで非公開、とかいうデータが必要な場合も……(頭わやになりそう)

Share on

END
END
@aiandrox

目次