データベーステーブル設計の基礎の基礎~エンティティの抽出・定義から正規化まで
適切な形でデータベースのテーブルを設計し、運用するには?テーブル設計に必要な初歩を日本MySQLユーザ会副代表の坂井恵さんが丁寧に解説します。
金融系アプリ、ゲーム、人工知能などなど……。どんな種類のシステムを開発する上でも、避けて通れない領域があります。データベースです。データを適切な形式で格納し、取り出す。単純明快ながらも奥深いこの仕組みは、多くのシステムの根幹を支えています。
しかし、適切な形でデータベースのテーブルを設計し、運用するのは簡単なことではありません。「良いテーブル設計」のためには知識と経験が不可欠です。今回は日本MySQLユーザ会の副代表である坂井恵さんに、これからテーブル設計に着手する方に向け、設計に必要な技術と、良い設計を作るための考え方を教えていただきました。
- フェーズ1:エンティティの抽出
- フェーズ2:エンティティの定義
- フェーズ3:正規化
- 設計の前に考えるべき「データベースの本質的な目的」
- 「すごい人の断言」は精査しよう
- データベースの楽しさを知ってほしい
フェーズ1:エンティティの抽出
エンティティ(実体)とは、ある共通項を持ったデータの集合体のこと。テーブル設計においては、なによりもまず、「エンティティをいかに設計するか」が重要です。このフェーズでは「システムに必要なエンティティ(=データ)は何か?」を洗い出していきます。
物理的なエンティティの例:社員、店舗、商品など
概念的なエンティティの例:購入履歴、進捗状況など
最初は大きいものから抽出していく
──エンティティを抽出する際、坂井さんはまず何から着手しますか?
坂井 このフェーズにおいては「なるべく大きな要素から抽出していく」のが大切です。仮に「車をWeb上で販売するサービス」のデータベースを設計するならば、私なら「車」と「顧客」という粒度の大きなエンティティから考えていくでしょう。
──「メーカーは何か?」「顧客の年齢や居住地は?」など、つい細部が気になってしまう方もいると思います。このフェーズでそうした情報を検討する必要はないのですか?
坂井 そうしたアイデアが途中で浮かぶこともあると思います。でも、エンティティ抽出の段階で細部から考えてしまうと、情報の粒度がそろわなくなってしまいますし、不要なエンティティを無駄に検討してしまう可能性も高いです。
とはいえ、途中で浮かんだ「こんな情報が必要かもしれない」というアイデア自体は価値が高いので、それらは別途メモしておき、後続のフェーズで利用すればいいと思います。
エンティティ抽出と要件定義は表裏一体である
──大きな単位でエンティティを抽出したら、次は何をすべきですか?
坂井 「エンティティ同士でどんな出来事・状態変化が起きるか?」を考えていくといいですね。
車をWeb上で販売するサービスの場合、「お客さんが車を注文する」という出来事は絶対に起こりますし、取引の記録を残しておかなければマズいですよね。その出来事そのものが、1つの概念的なエンティティになります。
つまり、システムの登場人物(物理的エンティティ)を大きな塊として洗い出した後、それらがシステム内でどう使われるのか、どんな情報を残す必要があるのかを考えていきます。エンティティ抽出のプロセスは、ほぼ要件定義そのものなんです。
──とはいえ、それらの要件を洗い出すのは、慣れていないと大変な作業に思えます……。
坂井 これらを考えるコツがあります。「システムがどう使われるのか、脳内で入念にシミュレーションすること」「要件の不明点を丁寧に潰していくこと」です。
システムの各種ユースケースをシミュレーションしていくと、「こんな事態が発生した場合、こういうデータが必要だ」とか「この情報は不要ではないか」などが分かってきます。
例えば、購入プロセスのなかで「仮注文」「本注文」「入金完了」といった状態変化があるならば、「その状態を何らかの方法で残さなければいけない」ということが見えてきます。必然的に、必要なエンティティも理解できるわけです。
また、シミュレーションのなかで要件の不明点が出てくるので、要件を決定する人にヒアリングをし、それらの点を一つ一つクリアにしていきます。
フェーズ2:エンティティの定義
各エンティティがどのようなデータ(カラム)を保持すべきかを定義するフェーズ。リレーショナルデータベースにおいてエンティティはテーブルで表現されるため、エンティティの定義とはテーブル定義とほぼ同義といえます。
必要なカラムは「起こり得るユースケース」「最終的にアウトプットすべき内容」をベースに導き出す
坂井 エンティティ抽出後、エンティティにどんなカラムが必要かを洗い出していきます。車ならば、メーカーや車種、色、年式などです。先ほど話したような、注文状況などの情報も持たせる必要があるかもしれません。
──洗い出しの漏れを少なくするには、どうすればいいですか?
坂井 くり返しになってしまいますが、さまざまなユースケースをシミュレーションすることが、このフェーズでも重要になります。
「もし注文がキャンセルされたら」「もしお金が支払われなかったら」など、多種多様なパターンを考えてみてください。そうすることで、それらの状態を管理するためにどんなカラムが必要かが見えてきます。
また、「画面・帳票にどんな内容が表示されるか?」を考えるのも良い方法です。表示される項目はデータベースに由来することが多いので、エンティティを定義する上で大きなヒントになります。
「注文がキャンセルされた」というユースケースをもとに、必要なエンティティを検討する場合、以下のように状態変化に応じて、データベースがどのような情報を保持すべきかを考えることで、必要なエンティティが見えてくる。
↓
「注文がキャンセルになった」という状態と、
顧客への返金先などを保持しておく必要がある
↓
注文の状態を持つエンティティが必要ではないか?
顧客のエンティティに口座番号を持っておくべきでは?
↓
具体的なテーブルやカラムに落とし込む
カラム名のシノニム・ホモニムには気を付ける
──「エンティティの定義」のフェーズでは、テーブルのカラム名も悩ましいポイントです。命名する上で大事なことはありますか?
坂井 カラム名のシノニム(呼び名が異なっているが、同じ意味を持つ語)・ホモニム(呼び名が同じだが、異なる意味を持つ語)には注意してほしいです。
シノニムとしてよくあるのは、
- 顧客情報を持つ「customer」テーブルの顧客名を持つカラムを「customer_name」という名前にする
- 「customer」テーブルとの関連付けのため、別のテーブルに「kokyaku_name」という名のカラムを持たせる
といった例です。これを避けなければいけません。なぜなら、「『customer』テーブルにある『customer_name』と別テーブルにある『kokyaku_name』は同じものだ」という前提知識を、エンジニアが知る必要が出てくるからです。
この場合は、両テーブルのカラム名を「customer_name」に統一しましょう。
同様に避けるべきケースとして、カラム名のホモニムがあります。例えば、ある店舗のデータベースのなかに「取引先企業の情報を持つテーブル」と「一般顧客の情報を持つテーブル」があるとします。
両テーブルの「顧客(取引先 or 一般客)名」のカラム名を「customer_name」にしてしまう、というのが一番マズいケースです。同名にもかかわらず意味が異なるため、エンジニアが混乱してしまいます。
「同じ情報を持つカラムは同じ名前にする」「異なる情報を持つカラムは異なる名前にする」のがネーミングの原則です。
日本語ローマ字表記のテーブル名・カラム名は悪か?
ネーミングに関して「日本語ローマ字表記のテーブル名・カラム名をつけてはいけない」と言われることがあるが、必ずしもそうではないと坂井氏は語る。なぜなら、テーブル名・カラム名において何より重視すべきは「開発メンバーの大多数が意味を理解できることだから」とその背景を説明する。
例えば、英語の辞書を参照しながら見慣れない英単語をテーブル名やカラム名につけた場合、エンジニアが十分に理解できないまま作業することになり生産性は落ちてしまう。それよりも、誰もが理解できる日本語ローマ字表記を使った方が生産性が高くなるというわけだ。
逆に、チームに外国人が多いなど、日本語が理解できないメンバーもテーブルを利用するならばむしろ英語名で統一すべきだと付け加える。いずれにしても重要視すべきは「どんな人がその名前を読むのかを考慮すること」だと坂井氏は強調する。
フェーズ3:正規化
正規化はより優れた設計にするために、各テーブルに持たせるデータの種類を最適化していくフェーズです。「あるテーブルが持っている情報のうち、特定の情報のみを別テーブルに切り出す」という方法を採ることが一般的です。
「独立して存在すべきデータは何か?」を考える
──正規化すべきデータとそうでないデータの基準を教えてください。
坂井 まず念頭に置いてほしいのは、正規化そのものを目的にしてはいけないということです。そうではなく、目指すべきは「データが壊れない設計」なんです。
──どんな場合に、データが壊れるのでしょうか?
坂井 INSERT
やUPDATE
、DELETE
などのデータ更新処理によって、データの不整合が起きてしまう状況のことです。そうした事態を防ぐため、正規化を行います。
例えば、ある講義の受講者を管理するテーブルがあるとします。正規化しない場合、受講者のIDや名前、講義のIDや講義名といった情報を全てこのテーブルで保持することになりますよね。
このなかに「受講者が1人しかいない講義」があるとして、その受講者が申し込みをキャンセルした途端に、「講座の情報そのもの」が消えてしまうんです。しかし、本来は講座情報だけは単体で残っていなければなりません。これが、正規化しない場合にデータが壊れるケースです。同様のデータ不整合が、INSERT
やUPDATE
などでも起こり得ます。
──受講者と講義の情報は別々のものなのに、片方の変更がもう片方に影響してしまうのは不便ですね。
坂井 はい。そのため、正規化により「他のデータの状態に依存せず、独立して存在してほしいデータ」を別のテーブルに移します。この場合は、受講者と講義の情報を別のテーブルに分けるわけです。
正規化によりデータが壊れるケースを考慮する
──逆に、正規化してはいけないケースはありますか?
坂井 あります。「ある時点のデータを、そのまま残しておく必要がある」ケースです。
例えば、商品情報を持つ「商品マスタテーブル」と、商品の売上履歴を持つ「売上履歴テーブル」があり、両テーブルが「商品ID」「商品名」「価格」の情報を持っているとします。
なんでもかんでも正規化すべきと考えて、後者のテーブルが持つ「商品名」「価格」を削除してしまうとマズいです。
──どうしてですか?
坂井 商品は「名前」や「価格」が将来的に変動することがあるからです。もともと1,500円で販売されていた商品が、あるタイミングで2,000円になるようなことも起こり得ます。
正規化してしまうと、その価格変動により過去のデータも「2,000円で購入された」ことになってしまうため、データの不整合が起きてしまうんです。マスタテーブルは最新の情報を持つもの、履歴テーブルは過去のログ・その時点のスナップショットを残すものなので、そもそも両者が持つべきデータの性質が異なります。
正規化することが目的になり、美しく分割されたテーブル設計に酔ってはいけません。やるべきことは正規化ではなく“壊れない設計”です。その本質を理解するのが、良い設計の足がかりです。
坂井恵さんの推薦書
リレーショナルデータベースやSQLを学ぶにあたり、『リレーショナルデータベース入門(著:増永良文 刊:サイエンス社)』『プログラマのためのSQL(著:ジョー・セルコ 監修・翻訳:ミック 刊:翔泳社)』の2冊を読んでおくといいと坂井さんは推奨する。
前者は「リレーショナルデータベースとは何か?」についての根源的な知識を得ることができる。坂井氏も翻訳文の査読(レビュー)に協力した後者は、SQLの基礎的な考え方から超絶技巧に至るまでありとあらゆる知識が学べる。
設計の前に考えるべき「データベースの本質的な目的」
──マインド面では、何を身に付けておくべきですか?
坂井 読者の方々に心掛けてほしいのは、データベースが持つ「本質的な目的」を見失わないでほしいということです。
多くのエンジニアは、データベースについて一定の知識が身に付くと、小手先のテクニックばかりを重視するようになってしまいます。どのようにエンティティの抽出をするか、いかにテーブルの正規化をすべきかなど。そうしたノウハウは重要なものですが、盲信してはいけません。
忘れないでほしいのは、データベースとは「データを安全に壊れないように保存してくれて、高速に取り出せるツールである」ということ。この本質が何よりも重要です。これを達成できているならば、ツールとしての役割は十分に果たせています。
さまざまな技術カンファレンスや書籍には、テーブル設計のノウハウが溢れており、それらを学ぶ過程で手段が目的化するケースは少なくありません。そうなってしまわないよう、気を付けてほしいと思います。
「すごい人の断言」は精査しよう
──一定のスキルを持っていると、「スキルを使うこと」が目的化してしまうこともあるかもしれませんね。
坂井 それに関連して、気を付けてほしいのは「すごい人の断言に惑わされるな」ということです。インターネット上で、時折、断定的な口調でテーブル設計の情報発信がされていることがあります。
例えば、「テーブルに削除フラグのカラムを設けるべきではない」などです。これは、安易に、機械的につけるべきではないという意味です。この理由はなぜかというと、無駄なレコードが増えてデータベースが重たくなってしまうのと、データをSELECTする際にWHERE句条件に削除フラグを指定する必要があるため、記載漏れがあった場合にバグの原因になるからです。
こうした提言は、基本的に正しいです。しかし、こうした断言の「結論」だけを真に受けて、理由を理解せず無条件に受け入れるのは良くありません。重要なのは「発信者はなぜ、それらの提言をしたのか」「自社サービスでそれらを遵守する必要があるのか」を考えることです。
──自分たちの用途にマッチしているか、が何より重要なのですね。
坂井 ただし、自社の開発体制にだけ最適化し、他のエンジニアが言っていることを聞き入れない、というのもそれはそれで良くありません。大事なのは「グローバルで使われている手法を理解した上で、ローカルに順応する」ということです。
きちんと勉強し、高いレベルを持ったエンジニアたちがどんなことをしているのかは把握しておく。その上で、自分の置かれている環境を考慮し「ここまではやるけれど、ここまではやらない」という方針を決めることが重要です。
データベースの楽しさを知ってほしい
坂井 最後に話しておきたいのは「データベースは本当に楽しいものだ」ということです。
システム開発においてデータベースは裏方ですし、あまり表には出てきません。動くアプリケーションを作るような世界とは違い、求められる作業も地味かもしれません。けれど、世の中にあるサービスのほとんどは、データベースがあるからこそ動いています。ぜひ、データを扱うことに興味を持ってほしいです。
私がデータベースに興味を持ったのは、ある“感動”がきっかけでした。まだ若手エンジニアだった頃に、SQLクエリのチューニングに携わったのです。
実行し終えるまでに10分程度もかかる重たいクエリだったのですが、チューニングし続けた結果、10秒くらいまで短縮できました。「アイデア1つでこれほど改善できるのか」と感動し、以来データベースにのめり込んだのです。
若いエンジニアの方々には、ぜひ日常的にデータベースに触れて、楽しさを見出してほしい。この記事を読んでくれた方が、データベースにハマってくれたら嬉しいです。
取材・執筆:中薗昴(サムライト)/写真:鈴木智哉