正規化

テーブルとテーブル間のリレーションシップなど、データベースの論理設計は、リレーショナル データベースを最適化するうえで最も重要な課題となります。データベースの効果的な論理設計により、データベースとアプリケーションが最適なパフォーマンスを発揮するための基礎が築かれます。データベースの論理設計が不十分な場合は、システム全体のパフォーマンスを低下させる可能性があります。

正規化の利点

データベースの論理設計を正規化するには、定型的な手法によりデータを複数の関連テーブルに分割する必要があります。複数の幅の狭い (列数の少ない) テーブルで構成されていることが、正規化されたデータベースの特徴です。少数の幅の広い (列数の多い) テーブルで構成されたデータベースは、正規化されていないデータベースと言えます。正規化規則の説明とデータベースのデザインへの規則の適用例については、「Introduction to Database Design Video (データベース デザインの概要ビデオ)」を参照してください。

多くの場合、適度な正規化によりパフォーマンスは向上します。SQL Server クエリ オプティマイザーは、適切なインデックスを使用できるときに、テーブル間に高速で効率的な結合を効率よく選択できます。

正規化には次のような利点があります。

  • 並べ替えとインデックスの作成の高速化。

  • 多数のクラスター化インデックス。詳細については、「クラスタ化インデックスの設計ガイドライン」を参照してください。

  • 幅が狭い、よりコンパクトなインデックス。

  • 1 テーブルあたりのインデックス数の削減。INSERT、UPDATE、および DELETE の各ステートメントのパフォーマンスが向上します。

  • NULL 値の数の削減および一貫性が失われる可能性の低下。その結果、よりコンパクトなデータベースを構築できます。

正規化の程度が増すと、データ取得に必要な結合の数が増え、より複雑になります。過剰な数のテーブル間に複雑なリレーショナル結合が多すぎると、パフォーマンス上の障害になる場合があります。正規化が適度に行われていれば、5 つ以上のテーブルを結合するようなクエリが必要になることはほとんどありません。

データベースの論理設計が既に固定されており、全体の再設計が現実的ではない場合もあります。しかし、そのような場合でも、大きなテーブルを選んで、いくつかの小さなテーブルに正規化できることもあります。ストアド プロシージャからデータベースへアクセスする場合、このスキーマ変更はアプリケーションに影響を与えずに行うことができます。そうでない場合はビューを作成して、アプリケーションにはスキーマ変更が見えないようにすることができます。

優れたデータベース設計の実現

リレーショナル データベースの設計理論では、データベースを最適に設計するために必要な属性と不要な属性を正規化規則によって識別します。正規化規則の詳細をすべて説明することは、このトピックで扱う範囲を超えています。ここでは、データベースの適正な設計に役立ついくつかの規則についてのみ説明します。

  • テーブルに識別子を追加する。

    データベースの設計理論の基本規則は、各テーブルに一意な行識別子、つまり 1 つのレコードをテーブル内の他のどのレコードからも区別する 1 つの列または列のセットを追加することです。テーブルごとに ID 列を追加してください。このとき同じ ID 値を複数のレコードに使用しないでください。テーブルの一意な行識別子の機能を果たす列がテーブルの主キーです。AdventureWorks2008R2 データベースでは、各テーブルに主キー列として ID 列が含まれています。たとえば、VendorID は Purchasing.Vendor テーブルの主キーです。

  • 1 つのテーブルには 1 つの型のエンティティを表すデータだけを格納する。

    1 つのテーブルに格納する情報が多すぎると、テーブル内のデータ管理の効率と信頼性が低下します。AdventureWorks2008R2 サンプル データベースでは、販売注文情報と顧客情報が別のテーブルに格納されています。1 つのテーブルに販売注文情報と顧客情報の両方を格納する列を作成することもできます。しかし、その場合は次のような問題が起こります。顧客情報 (名前と住所) を販売注文ごとに重複して追加し、格納しなければなりません。このため、データベース内の記憶領域を余分に使用します。顧客の住所が変わった場合は、販売注文ごとに住所を変更する必要があります。また、顧客の最後の販売注文が Sales.SalesOrderHeader テーブルから削除された場合、この顧客の情報は失われます。

  • テーブルに NULL 値を許容する列を作成しない。

    テーブルには NULL 値を許容する列を作成できます。NULL 値は値がないことを意味します。特殊なケースでは NULL 値が便利なこともありますが、NULL 値を許容する列は極力使用しないようにしてください。これは、NULL 値を許容する列は、特殊な処理が必要でデータ操作が複雑になるためです。NULL 値を許容する列が複数あり、その列の一部の行に NULL 値がある場合は、これらの列は別のテーブルに格納して、プライマリ テーブルにリンクしてください。2 つの個別のテーブルにデータを格納すると、プライマリ テーブルの設計が単純になり、NULL 値を格納する必要がある場合にもそのまま対応できます。

  • テーブルに繰り返し値または繰り返し列を作成しない。

    データベース内のアイテムのテーブルには、個別の情報の値のリストを入れないでください。たとえば、AdventureWorks2008R2 データベース内のある製品は複数のベンダーから購入されている可能性があります。Production.Product テーブルにベンダー名を格納する列があると、これが問題になります。1 つの解決策としては、すべてのベンダーの名前をこの列に格納します。ただし、この方法では、各ベンダーを一覧で表示することが難しくなります。別の方法としては、テーブルの構造を変更して、2 番目のベンダーの名前を格納する列を 1 つ追加するという方法も考えられます。ただし、これでは 2 社のベンダーの名前しか格納できません。また、製品のベンダーが 3 社である場合はさらに 1 つ列を追加する必要があります。

    1 つの列に値のリストを格納する必要がある場合や、1 つずつデータを格納する列を複数 (TelephoneNumber1、TelephoneNumber2 など) 作成する場合は、同じデータを複製して他のテーブルに格納し、主テーブルにリンクする方法をとってください。AdventureWorks2008R2 データベースには、製品情報を格納している Production.Product テーブルと、ベンダー情報を格納している Purchasing.Vendor テーブルがあり、さらに Purchasing.ProductVendor という 3 番目のテーブルがあります。この 3 番目のテーブルには、製品の ID 値と製品のベンダーの ID のみが格納されています。この設計では、ある製品のベンダーが何社あってもテーブルの定義を変更せずに済むだけでなく、ベンダーが 1 社だけの製品に未使用の記憶領域を割り当てずに済みます。