SQLアンチパターン自分用まとめ

「SQLアンチパターン」というオライリーのRDBの名著を自分向けにまとめました。
各章ごとに問題→解決策→感想を記載。リンクはアフィなし。
あくまで自分向けだけど、自分にとって世界一分かりやすいまとめを目指しました。
あと各章タイトルは、本家のタイトルが正直オシャレ目指しすぎて意味不明なので自分が勝手につけ直しました。

全体的な感想
見直すと、感想「当たり前やろがい!」が多いけど、よく考えると「それは一般的にアンチパターンとされていて、また具体的にはこのような理由で避けるべき」みたいな説明が常に自力でできるわけではないなとも思う。
ある種の権威主義みたいな所もあるかもしれないが、自分自身の力では「なぜダメか」を完璧には言えない所、この本の力(及び虎の威)を借りることができる、というのはある。


1章 正規化しろ

問題
多対多を1つのカラムにカンマ区切りIDを入れて解決しようとするな

解決法
中間テーブルを使え

感想
当たり前やろがい!!!

2章 木構造難しい

問題
木構造をRDBで扱うのは簡単じゃないぞ

解決策
色々あるがツリー構造全体をRDBに保存する閉包テーブルが総合的にベスト

感想
難しそうだなあと思ったがSNSの投稿が木構造になるし、実用性高そう。
いつか必要を感じたら再読

3章 auto increment idについて

問題
自動インクリメントの整数型idをプライマリキーにするのは冗長になるしデータ不整合を招くこともあるぞ

解決策
自然キーとして一意なものがあるならそれを使え
複合主キーもあり
場合によっては自動インクリメント整数型idしかないこともある

感想
この問題は調べたことがある。
UUIDを推奨するこの記事を暫く真に受けていたが、

こういうパフォーマンス上の問題もあるらしく、

UUIDv7ならパフォーマンスの問題もなんとかなる、みたいな話もある。

最初の記事しかちゃんと読んでない。あとでちゃんと調べる。

4章 外部キー制約使え

問題
外部キー制約を使わないとデータ不整合が起きるよ

解決策
外部キー制約を使え

感想
当たり前やろがい!

MongoDBは外部キー制約がないこともあってデータ不整合を防ぐのが苦しかったなと思う。
DBスキーマもないし、RDB/NoSQLって静的型付け言語/動的型付け言語みたいな感じがある

5章 1つのカラムに複数の意味

問題
1つのカラムが複数の意味を持つようにするとデータ不整合を防ぐのが難しいしSQLも難しくなる

解決策
具体的なやり方は色々あるが、大原則としてテーブルを分けろ。
違うものは違うテーブルとして扱う。これ鉄則。

感想
当たり前やろがい!

6章 親テーブルが複数

問題
親テーブルが複数あるような設計にすると外部キー制約がかけられなくてデータ不整合を防ぐのが難しくなる

解決策
テーブルを分ける。具体的には、
・親と子の共通の親テーブルを作り、すべてがその直接の子テーブルになるようにする
・1つの子に2つの親がいるなら、親子の間に多対多の中間テーブルを挟み込む
のどっちかを取ればOK

感想
これはなるほど感すごい

7章 同じ意味のカラムが複数

問題
1つのバグが複数のタグを持つ、というようなテーブル構成で、
バグテーブルの中に3つのタグカラムを入れて作るようなことをすると、
値の検索・追加・削除が難しいし、タグ名の一意性は保証されないし、
つけられるタグに制限が無意味にかかる。

解決策
テーブルを切り分けよう

感想
当たり前やろがい!!

8章 同じ意味のテーブルが複数

問題
・2002年のテーブル
・2003年のテーブル
・2004年のテーブル
・…
みたいなテーブルの分け方をすると、
「どこに保存すればいいのかの判断が難しい」
「データ整合性を保つのが難しい」
「年をまたぐ日付修正をかけるのが難しい」
「一意性が保証されない」
「SQLが書きにくい」
「テーブル定義を修正したい時一気に全部やる必要がある」
「外部キー制約がかけられない」
など、問題の山。

解決策
水平パーティショニングか、
垂直パーティショニングか、
正規化
どれがいいかは場合による

感想
これも「当たり前やろがい!」としか思えない。
考えるまでもなく「こんなことしたら間違いなく問題が山程起きるしやばすぎるだろう」と直感でわかる

9章 小数点の精度に注意

問題
会計計算や金融計算とかで小数点の細かい計算を行う時、
コンピュータは本質的に二進数計算しかできない関係上、
RDBでFLOAT型を使うと必ず丸め誤差が起きる。
僅かな誤差だと思って油断するなかれ。それが致命的な違いを出すことはよくあるし、
それが許されないシステムは珍しくない

解決策
NUMERICデータ型を使う。
精度に制限がある代わりに、その範囲では絶対に間違った計算になることのないデータ型らしい

感想
これは知らなきゃそこにこんな問題が存在してることにすら気付けなかった気がする。
読んで良かった章

10章 種類をカラムの値で表すと

問題
種類を表すカラムをenum型とかで定義すると、
中に何が入ってるのか取得しづらいし、
新規種類追加もマイグレーションが必須になるし、
一度決めると変更が難しい

解決策
種類を表す専用のテーブルを作ろう

感想
実務で全く同じ問題にハマり、ググってこの解決策に行き着いてうまく行った経験があるので、
せやな~ってなった

11章 画像はDB外部に保存する?

問題
画像をRDBの外側のサーバーシステムに保存させると、
データ不整合が起きるし、トランザクションの外側にあるからトランザクションで保護されないし、
バックアップ対象からも外れるし、
色々大変なことがおきる

解決策
別テーブルに分けてRDBのBLOBで扱う。
別テーブルに分けたほうが良い。画像は重いし毎回必要なわけじゃないので、軽くSELECT文などを回すために。

感想
これは現代だと「AWS S3に分けるか否か」みたいな話だろうか。
気持ちは分からなくはないけど、そこまでシビアにならなくて良いケースなら自分はAWS S3でよしとしてしまうかも

12章 インデックスには気をつけろ

問題
インデックスをやみくもに貼るとむしろパフォーマンスは下がるし、
インデックス貼っておけば検索が常に速くなるわけでもない

解決策
・インデックスの性質を十分理解する
・アプリケーションがどのような検索を要求してるかを理解する
・計測もする
・MENTORの原則に基づいてインデックスを貼るかを決定する

感想
普通に難しいっすね。自分まったくインデックスのこと知らないなと理解させられた

13章 NULLにご用心

問題
NULLがSQL上でどういう扱いなのかを理解していないと、
SQLの実行結果で何を間違えてるのかに気付けない

解決策
NULLの性質を十分理解した上で、
適切にNULLを扱えるようになる。
また必要ならNOT NULL制約をかける

感想
インデックスの話同様、本質的に要約しづらいが、大事な話

14章 GROUP BYほんとに分かってる?

問題
SQL文のGROUP BYの性質を理解してないと、
結果を取得するのが不可能なSQLを書いてしまう

解決策
GROUP BYの単一値の原則を理解する。
理解していれば他に色々ある解決策もわかるはず。

感想
SQLから離れて久しいが、これは体感としてなんとなく覚えてる。
ただSQL自体を復習しなくてはな、と思わされた

15章 ランダム抽出は意外と難しい

問題
RDBからランダムな結果を抽出する、というのは難しく、
全部取得してソートする、というやり方だとパフォーマンス上負荷がデカすぎる

解決策
色々やり方があるが、大体auto incrementの主キーがある前提。
割とUUID主キーでも現実的かなというのは
「すべての主キーリストを取得して、アプリ側でランダムに1個選択してそれでSELECT」かな。
LIMIT文で、行のオフセットを指定して取得できるので、
行数をSQLで取得→アプリ側でランダムに生成したオフセットを取得→SQLでそれを指定して呼び出し
みたいなのもあるらしい。

感想
思ったより難しいしめんどくさそう

16章 全文検索をLIKEでやるな

問題
全文検索をRDBのLIKEでやろうとするのは、かなり早々に限界が来る

解決策
全文検索は全文検索専用のDBを使いましょうね

感想
当たり前やろがい!

17章 SQLはシンプルに

問題
1つのSQLに複雑すぎるタスクを実行させようとすると、
可読性が悪いし書く難易度も高すぎるので大体失敗する

解決策
小さいタスクに分けて複数のSQLにしよう

感想
当たり前やろがい!

18章 ワイルドカードは非推奨

問題
SELECT文で
SELECT * FROM TABLE
このワイルドカードはなるべく使わない方が良い。
なぜなら、「結果がテーブル定義を変えるごとに変化する」という点で、
分かりづらいバグを将来埋め込むリスクが常にあるから

解決策
毎回SELECTのカラムは明示的に指定しよう

感想
なんとなく分かる話だけど、具体的にどうアウトかという話までは深く考えたこと無かったな

19章 パスワードは厳重に

問題
パスワードを平文で保存すると色々やばい

解決策
ソルト付きハッシュを使おう、
あと通常のセキュリティ方針に則ったやり方でパスワードの保存と再設定を管理しよう

感想
当たり前やろがい!

20章 それってSQLインジェクションでは?

問題
ユーザーから受け取った何かをSQLに埋め込む形のコードを書くと、
SQLインジェクションを許してしまう

解決策
プリペアドステートメントが最有力だが、
ちゃんとそれがなぜ・どのようにSQLインジェクションから守ってるのかについてはよく知り、理解しておいた方が良い

感想
一見当たり前だけどきちんと知るべき話題だった。

21章 欠番埋める意味無し

問題
autoincrement idで削除して空いた欠番を無理に埋めると、
すさまじいデータ不整合がお前を待っている

解決策
欠番を埋める意味はないのでそのままにしておく

感想
当たり前やろがい!

22章 例外処理しろ!!!

問題
アプリ側でRDBに関する異常系の例外処理をきちんと書かないと、
アプリ側はなんだかよくわからない理由で動かなくなってしまう

解決策
例外処理をしろ

感想
当たり前やろがい!!

23章 SQL/RDBも品質保証対象です。

問題
SQLをコードと別枠みたいに捉えてると、
すごい大きな問題が突然顕在化する

解決策
SQL文等をコードと同様に品質保証対象にするべき。
具体的には、
・ドキュメントを残す(ER図なども)
・バージョン管理対象にする
・テスト対象にする

解決策
当たり前っちゃ当たり前だけど、完璧にできてる人はそう多くはないかも

24章 MVCアーキテクチャの注意点

問題
MVCアーキテクチャで、
モデルが純粋にRDBの基本的操作しか担当しないようなコードを書いてしまうと、
他のどっかがもっと難しいDB操作ロジックの負担を背負い込むことになるが、
そうするとコードが極端なぐらい複雑化して破滅する

解決策
色々やり方はあるが、
個人的に一番好きなのは
「ビジネスロジックはビジネスロジックだけ扱う層を作ってそこでだけやる」
かな。クリーンアーキテクチャとかが前提にしてるし本にも載ってる

感想
今となっては「当たり前やろがい!」と言えるが、
自分的に分かってきたのは最近だし、業界的に当たり前になったのもこの本が出て以降な気はする

25章 実運用の大変さ

問題
実運用で何が起きるのかについてなんにも考えてない結果、
実運用時に致命的トラブルが起きてしまう

解決策
一言では言えないが、
パフォーマンスのベンチマークを本番想定で取るとか、
テスト環境を早い段階で用意しておくとか、
例外処理ちゃんとやるとか、
バックアップ取っておくとか、
可用性を意識して災害時も想定しておくとか

感想
本番環境は大変だ。

付録 正規化

適当に要約すると、
実質的にテーブル内で従属関係がある時、
その冗長性を取り除く形でテーブルを切り分けるということを適切にやらないと、
結構致命的なデータ不整合の元になっちゃうので気をつけようねという感じ
ただ割と当たり前のことを言ってるだけではある。

この記事が気に入ったらサポートをしてみませんか?