データベース設計におけるアンチパターンを覚え、それを回避する方法を身につけておけば、あなたの開発するシステムはとてもシンプルになり、生産性、保守性、そしてシステムの寿命を大きく伸ばしてくれます。
本記事では、データベース設計におけるアンチパターンをいくつかご紹介します。
アンチパターンとは?
アンチパターンとはソフトウェア開発によくある失敗パターンを整理し、それを回避する方法をまとめたものです。元々は1995年にアンドリュー・コーエンが提唱したもので、コンピュータの歴史からみると比較的新しめの概念です。
データベース設計はシステム全体に非常に強く関わりがあるので、ここで失敗するとプロジェクト全体に響くことになります。先人が残したアンチパターンを知っておくことで回避できる危険は回避しましょう。
ちなみに、アンチパターンの反対の概念として「デザインパターン」というのもあります。デザインパターンはソフトウェア開発における良い設計パターンを整理しまとめたものです。
アンチパターン(1) 配列データを保持する
RDBMSの基本の一つに正規形という概念がありますが、その中で最も基本とされる第一正規形すら破壊してしまうのが「配列データの保持」です。
第一正規形は簡単に言うと「一つのセルには一つのデータのみを格納する」というシンプルなルールで作られたテーブルのことです。なので、本来配列データをセルに入れられてしまうのは問題があるのですが、1999年のSQLの標準規格改定から「配列型」というデータ型が登場しています。
ここで言う配列とはJavaやPHPで使う配列と同じ意味です。なので、配列をセルに格納すると第一正規形すら守れないテーブルが出来上がってしまいます。
メンバーが配列になっているテーブル
チームID | 代表者 | メンバー(配列) |
1 | 佐藤 | [伊藤, 加藤] |
2 | 鈴木 | [白木, 柏木, 茂木] |
3 | 田中 | [角田, 善田] |
実は配列データを格納できることもメリットがないわけではないです。JavaやPHPで作ったアプリケーション側からのデータを直接テーブルに格納できるので、うまく使えば便利ではあります。
しかし、データベースの厳密性を大きく損なうことになりますので、やはりあまり使うべきではないと個人的に思います。このような配列を表現したい場合は、行持ちでデータを複数行作ることで第一正規形を守ることができます。
チーム
チームID | 代表者 |
1 | 佐藤 |
2 | 鈴木 |
3 | 田中 |
メンバー
チームID | メンバー |
1 | 伊藤 |
1 | 加藤 |
2 | 白木 |
2 | 柏木 |
2 | 茂木 |
3 | 角田 |
3 | 善田 |
アンチパターン(2) 意味が伝わらない列名とダブルミーニング
汎用的な列を作るのは絶対にやめましょう。
例えば次のテーブルを見てください。
顧客
顧客ID | 顧客名 | 住所 | その他 |
1 | 〇〇株式会社 | 東京都千代田区 | 080-xxxx-xxxx |
2 | ××株式会社 | 東京都中央区 | 080-□□□□-□□□□ |
3 | 株式会社□□ | 大阪府大阪市北区 | 530-xxxx |
4 | 合同会社△△ | 北海道札幌市 | 001-□□□□ |
注目するのはその他列です。その他列はたいていの場合「列を追加するのは大変。けど後々柔軟に列を追加できるようにしたい」という願望から生まれます。その結果、上2行のその他列には電話番号が、下2列には郵便番号が入っています。後から列を追加するのは大変...あ、「その他」の列なら入れてもいいんじゃ?と考えた結果です。
こうなると、システム上で各行ごとにその他のセルに何のデータが入っているかを逐一確認する必要が出てきてしまい、バグの温床にもなってしまいます。その他の中に何のデータが入っているかを資料化する必要も生まれますし、良いことは全くありませんので、汎用的な列は作らないのが吉です。
こういった場合はちゃんとそれぞれのデータに応じた列を作ってあげましょう。
顧客
顧客ID | 顧客名 | 住所 | 電話番号 | 郵便番号 |
1 | 〇〇株式会社 | 東京都千代田区 | 080-xxxx-xxxx | |
2 | ××株式会社 | 東京都中央区 | 080-□□□□-□□□□ | |
3 | 株式会社□□ | 大阪府大阪市北区 | 530-xxxx | |
4 | 合同会社△△ | 北海道札幌市 | 001-□□□□ |
アンチパターン(3) 同じ構造のテーブルを一つにまとめる
前項の「意味が伝わらない列名とダブルミーニング」と似た内容になりますが、おそらくこちらの方が実際の現場でも遭遇する可能性が高いテーブルになります。
例えば、次の3つのテーブルがあったとします。
取引先
取引先ID | 取引先名 |
1 | A取引先 |
2 | B取引先 |
3 | C取引先 |
部署
部署ID | 部署名 |
1 | D部署 |
2 | E部署 |
3 | F部署 |
グループ
グループID | 部署名 |
1 | Gグループ |
2 | Hグループ |
これらのテーブル、見てわかる通り全て構造が同じで、keyとvalueが1対1で紐づいたような体系になっています。
「テーブルが増えて管理が大変になってきたな...」と思ったDB設計者がこの状況で考えることはたいていの場合「同じ構造のテーブルをくっつければいいんだ!」という発想です。
この発想の元作成されるのが次のテーブルです。
コード
コード区分 | コード | コード内容 |
SUP_CD | 1 | A取引先 |
SUP_CD | 2 | B取引先 |
SUP_CD | 3 | C取引先 |
DEP_CD | 1 | D部署 |
DEP_CD | 2 | E部署 |
DEP_CD | 3 | F部署 |
GROUP_CD | 1 | Gグループ |
GROUP_CD | 2 | Hグループ |
もうお分かりかと思いますが、このテーブルだとコード列とコード内容がダブルミーニングを起こしています。元々は取引先や部署など適切な名称が付いていましたが、1つのテーブルにする上で汎用的な名称を付けざると得なくなってしまいました。
ただし、この方法はコード区分さえ適切に指定できればコード列とコード内容の意味を正確に知ることができるので、先ほどのその他列に比べればデメリットは少なく、かつテーブルの数は減るので管理しやすくなるメリットもあります。
しかし、コードの最大長を統一する必要性が生まれることや、テーブルを分けた場合を比べてエラーが起こりやすい(例えばコード区分の指定を間違える可能性がある)などの問題は増えます。一長一短ではありますが、例えば後から追加した内容によってはコード列の最大長が足りなくなる等の問題も発生する可能性があります。
ダブルミーニングにもなりますし、基本的にはテーブルは分けて、どうしても必要な場合はテーブルを1つにすると良いでしょう。
まとめ
良いデータベース設計とは人によってマチマチで、アンチパターンも時と場合によっては必要になることも多く、一概に絶対にやってはいけない設計というわけではありません。しかし、「扱いが難しくなるデータベース構造」というのは必ず存在し、それらを先人達から学ぶことは無駄にはならないでしょう。
元々データベース設計は厳密性とパフォーマンスのトレードオフの中でいかに最適化できるか、というのが課題の一つでした。しかし、現代は大量のデータを高速に処理できるありがたい時代です。その背景からなるべく厳密性を重視した設計を行うことで、システムの寿命を伸ばしていけるはずです。
- カテゴリ: