「SQLアンチパターン」で後悔しないDB設計を学ぼう

最近、SQLアンチパターンという本を読みました。

SQLアンチパターン 第2版

リレーショナルデータベースを扱うシステム開発には、様々な場面で陥りやすい失敗(アンチパターン)が存在します。本書では、SQLやデータベース設計を深く掘り下げ、データモデリングやSQLクエリのロジック、データ駆動アプリケーションのコード設計におけるアンチパターンを紹介し、それらを回避するための実践的な方法を解説します。 ツリー構造や継承構造のテーブル設計、NULLを正しく扱う手法、ランダムに結果を返すクエリやグループ化を行うクエリのコツ、SQLインジェクションなどのセキュリティリスクからウェブアプリケーションを守る手法など、幅広いトピックを網羅します。 第2版では内容を大幅に改訂し、新規書き下ろしの章と15のミニ・アンチパターンが加わりました。 日本語版付録として、奥野幹也氏による書き下ろしのアンチパターン「砂の城」を「関連ファイル」からダウンロードできます。

https://www.oreilly.co.jp/books/9784814400744/

前々から気になっていた本ではあったのですが、最近第2版が出版されたので、これを機に読んでみることにしました。今回はその本のおすすめポイントや、個人的な気付きや学びなどを共有します。

読んだ感想

読む前は、少し難しそうな内容を想像していたのですが、実際に読んでみると思ったよりも読みやすく、意外とすんなり読み終えることができました。RDBとSQLの基本的な知識があって、多少の実務経験があればそれほど苦労せずに読むことができるかと思います。

SQLアンチパターンでは20種類以上のアンチパターンが掲載されています。「流石にこのパターンはやろうと思ったことないし、現場でもみたことない」というパターンもあれば、「これはよくやっちゃうなー」というパターンや、「最近は見かけないけど昔はよくあったなー…」というパターンもありました。

自分自身がよくやってしまうアンチパターンについては、今後の開発のための良い学びになりました。

アンチパターンを学ぶ意味

そもそもアンチパターンとは何でしょうか。
この本の中では以下のように説明されています。

アンチパターンとは、問題を解決しようとした結果、他の問題を生じさせてしまうような技法を指します。(書籍:「SQLアンチパターン 第2版」より)

ではアンチパターンを学ぶ意味は何でしょうか?

ソフトウェア開発の仕事は本質的に不確実性が高い仕事です。そのような不確実性に対処するために、様々な設計手法や開発手法が提唱されていますが、「この方法を用いれば確実にうまくいく」と言える万能な方法論は今のところ存在しません。 開発が進んで多くの課題に直面するにつれて、「こういう設計・実装方法にしておくべきだった…」と後悔する場面がきっと出てくることでしょう。私自身、設計や実装方法で後悔することは多くあります。
実務においては、開発期間中でスケジュールにも比較的余裕がある段階であれば、設計方針や実装方針を切り替えてやり直すことも可能かもしれませんが、一度リリースされてしまうと後から修正することが現実的に難しいケースも出てきます。

ソフトウェア開発において後悔を減らすには、後々問題を引き起こしやすい方法、いわゆるアンチパターンを知ることが大事です。先人達が経験してきた失敗を予め学んでおくことで、同じ失敗を未然に防ぐことができます。それがアンチパターンを学ぶ意味です。 アンチパターンを学んだからといってソフトウェア開発が必ずしも成功するとは限りません。しかし、多くのアンチパターンを知ることで、ソフトウェアの価値が下がるリスクを低減できます。

アンチパターンは様々な分野に対して存在しますが、「SQLアンチパターン」はその中でもRDB(リレーショナルデータベース)の設計、およびSQLの使用方法に関するアンチパターンがまとめられている本になります。

アンチパターンの紹介

ここでは、個人的に印象に残ったアンチパターン(というか、私がやりがちなもの)を2つほど紹介します。

IDリクワイアド(とりあえずID)

これは、全てのテーブルに「ID」列を追加し、それを主キーとして扱うというパターンです。 特定のWebフレームワークを使用していると、フレームワークデフォルトの仕様で「ID」カラムが主キー設定されるものが多く、その名残でフレームワークに依存しないテーブル設計をする場合でもついつい「ID」カラムを主キーに設定してしまいがちでした。

これがアンチパターンとされる理由の詳細は本に譲りますが、解決策としては、キーにわかりやすい名前をつけることを提唱しています。例えば、usersというテーブルがあり、IDを主キーにしたい場合、カラム名はidではなくuser_idのようにすることで、どのデータに対するIDなのかが分かりやすくなります。

※ 本書では「IDを持つこと自体」を否定しているわけではなく、
そのIDがドメイン上の意味を持たないまま乱用されることを問題にしています。

キー名を明示的にすることで、結合のSQLの記述を簡潔にできるメリットもあります。具体的には、SQLでテーブル間を結合する際に、USINGを使用した結合(自然結合、natural join)が使えるようになります。 例えば、companiesテーブルとusersテーブルを、company_idで結合したい場合は以下のように書けます。

SELECT *
FROM companies
JOIN users
USING (company_id)

キー名が異なる場合(companiesテーブルの主キーがidカラムである場合)は、ONを使用してどのキーを結合条件にするかを明示的に指定する必要があります。

SELECT *
FROM companies c
JOIN users u
ON c.id = u.company_id

USINGを使った結合は知識として知ってはいましたが、ほとんど使ったことがありませんでした。キー名を明示的にしておくことで、テーブル構造だけでなくSQLも分かりやすく保つことができそうです。これからはidという名前のキー名を避けようと思いました。

キーレスエントリ(外部キー嫌い)

これは、リレーションのあるテーブルに対して外部キー制約を使用しないというアンチパターンです。先の例で言うと、usersテーブルはcompany_idcompaniesテーブルと紐づいているように見えますが、テーブル定義上は外部キー制約を付けていないような状態です。 この場合、companiesテーブルには存在しないデータであっても、usersテーブルのcompany_id に不正な値を登録することが可能になってしまいます。例えば、companiesテーブルに、company_idが1と2のデータしか存在しない場合に、usersテーブルでcompany_idが3のデータを登録できる、という状態です。

このアンチパターンは、私自身やってしまいがちでした。 なぜ外部キー制約をつけないかというと、外部キー制約があると、整合性の取れたデータしか登録することができないため、テーブルの数が増えたときにテストデータを作るのが非常に面倒になる印象がありました。一部の機能をテストしたい場合に、必ずしも整合性の取れたデータは必要ないケースもあるため、そのような場面での開発効率を考えると、外部キー制約が邪魔になるケースがありました。私が過去に経験していたプロジェクトでも、外部キー制約をあまりつけない設計をしているプロジェクトが比較的多く、そのような案件ではテストデータが作りやすく、開発しやすいイメージがありました。

しかし、今改めて考えてみると、当時と今では開発環境の前提が大きく異なり、外部キー制約を付けないメリットはあまりないかもしれません。最近は開発時にテストを自動化することが多く、テスト実行時に自動的に整合性の取れた初期データが作られるようにしておけば、外部キー制約によってテストがしにくいという問題も解消できます。そのため、外部キー制約を付けないことで不整合データが登録されてしまうリスクの方が、後々問題になる可能性が高そうです。

今後は外部キー制約は適切に設定するべきだなと思いました。

アンチパターンの気づき方

学びに関する有名な言葉で、「愚者は経験に学び、賢者は歴史に学ぶ」という格言があります。
アンチパターンを学ぶことは先人たちの失敗の歴史を学ぶことであり、ソフトウェアの価値を高めるために非常に大事だと思います。

一方、ソフトウェア開発の仕事は不確実性が高く複雑なものでもあり、経験からしか学ぶことできないことも多くあると考えています。個人の経験やチームの経験から、アンチパターンに気づき、知識に昇華することが重要です。
経験からアンチパターンに関する学びを得るためには、個人やチーム間で振り返りを実施し、気づきや学びを共有することが重要です。書籍やブログ記事などを通してよくあるアンチパターンを学びつつ、定期的に振り返りを実施して経験の中からアンチパターンを知ることで、ソフトウェア開発において失敗のリスクを下げられるようになると思います。

まとめ

  • アンチパターンとは、問題を解決しようとした結果、別の問題を引き起こしてしまう技法
  • アンチパターンを知ることで、失敗のリスクや後悔する確率を下げられる
  • 振り返りを実施してアンチパターンに気づけるようにする

DBやSQLに限らず、設計方針を考える際は、これがアンチパターンになる可能性がないかを一度立ち止まって考えるようにしたいと感じさせる一冊でした。

参考

SQLアンチパターン 第2版

リレーショナルデータベースを扱うシステム開発には、様々な場面で陥りやすい失敗(アンチパターン)が存在します。本書では、SQLやデータベース設計を深く掘り下げ、データモデリングやSQLクエリのロジック、データ駆動アプリケーションのコード設計におけるアンチパターンを紹介し、それらを回避するための実践的な方法を解説します。 ツリー構造や継承構造のテーブル設計、NULLを正しく扱う手法、ランダムに結果を返すクエリやグループ化を行うクエリのコツ、SQLインジェクションなどのセキュリティリスクからウェブアプリケーションを守る手法など、幅広いトピックを網羅します。 第2版では内容を大幅に改訂し、新規書き下ろしの章と15のミニ・アンチパターンが加わりました。 日本語版付録として、奥野幹也氏による書き下ろしのアンチパターン「砂の城」を「関連ファイル」からダウンロードできます。

https://www.oreilly.co.jp/books/9784814400744/