【DB】多様な製品タイプのためのテーブル設計パタン

## はじめに
この記事では、リレーショナルデータベースで製品データをモデリングするための5つの一般的なアプローチを探り、それぞれの長所と短所を強調し、ニーズに最適なアプローチを選択するためのガイダンスを提供します。

## 1. 単一テーブル継承 (Single Table Inheritance, STI)

**説明:**

各製品タイプが必要とする属性を一つのテーブルにまとめて保存する方法です。これにより、製品タイプによって意味のない属性が発生し、多くの属性にNULLが割り当てられます。

**長所:**

- 単純で直感的なテーブル構造
- 実装と理解が容易

**短所:**

- 多くのNULL値によるスペースの無駄
- 特定の製品タイプに対する制約条件を適用するのが難しい
- 多くの列があるため、パフォーマンスの問題が発生する可能性。

**例スキーマ:**

```sql
CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    type VARCHAR(50) NOT NULL,
    size VARCHAR(50),
    color VARCHAR(50),
    weight DECIMAL(10, 2),
    warranty VARCHAR(50),
    expiration_date DATE,
    -- ほとんどNULLとなる他の多くの列
);
```

## 2. クラステーブル継承 (Class Table Inheritance, CTI)

**説明:**

共通属性のためのメインテーブルと、各製品タイプの特定の属性のための追加テーブルを作成して保存する方法です。これにより、より正規化され効率的なスキーマを作成できます。

**長所:**

- NULL値が少ないため効率的なストレージ
- 制約条件とデータの整合性を適用しやすい
- 管理しやすく、拡張可能

**短所:**

- 完全な製品情報を取得するためにJOIN操作が必要
- 実装がやや複雑

**例スキーマ:**

```sql
-- メインテーブル
CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    type VARCHAR(50) NOT NULL
);

-- 追加テーブル
CREATE TABLE product_electronics (
    product_id INT PRIMARY KEY,
    warranty VARCHAR(50),
    voltage VARCHAR(50),
    FOREIGN KEY (product_id) REFERENCES products(id)
);

-- 追加テーブル
CREATE TABLE product_groceries (
    product_id INT PRIMARY KEY,
    expiration_date DATE,
    weight DECIMAL(10, 2),
    FOREIGN KEY (product_id) REFERENCES products(id)
);
```

## 3. 具体テーブル継承 (Concrete Table Inheritance, CTI)

**説明:**

各製品タイプごとに、共通属性と特定の属性の両方を含む別々のテーブルを作成して保存する方法です。

**長所:**

- 各製品タイプに対する単純なクエリ
- 制約条件とデータの整合性を直接適用可能

**短所:**

- 共通属性の冗長な保存
- 複数のテーブルで共通属性を管理および更新するのが難しい

**例スキーマ:**

```sql
CREATE TABLE product_electronics (
    id INT PRIMARY KEY,
    name VARCHAR(255) NOT NULL, -- 共通属性
    warranty VARCHAR(50) NOT NULL, -- 特定属性
    voltage VARCHAR(50) -- 特定属性
);

CREATE TABLE product_groceries (
    id INT PRIMARY KEY,
    name VARCHAR(255) NOT NULL, -- 共通属性
    expiration_date DATE NOT NULL, -- 特定属性
    weight DECIMAL(10, 2) -- 特定属性
);
```

## 4. シリアライズされた大容量オブジェクト (Serialized Large Object, LOB)

**説明:**

共通属性をテーブルに保存し、タイプ別属性をシリアライズされたオブジェクト（例：PostgreSQLのJSON）として別の列に保存します。

**長所:**

- 非常に柔軟で、任意の属性を保存可能
- 単純なテーブル構造

**短所:**

- シリアライズされたデータ内の特定の属性をクエリするのが難しい
- 検証と制約条件を適用するのが難しい

**例スキーマ:**

```sql
CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    type VARCHAR(50) NOT NULL,
    attributes JSON -- JSONデーター
);
```

**例JSONデータ:**

```json
{
    "warranty": "2 years",
    "voltage": "220V"
}
```

## 5. エンティティ属性値 (Entity-Attribute-Value, EAV)

**説明:**

属性をテーブルの列ではなく行として保存する方法です。製品のためのメインテーブルと、属性を行ごとに保存する別のテーブルがあります。

**長所:**

- 非常に柔軟で、属性を簡単に追加可能
- スキーマ変更なしで新しい属性を追加可能

**短所:**

- 制約条件とデータの整合性を適用するのが難しい
- 複雑で非効率的なクエリ
- 管理と保守が難しい

**例スキーマ:**

```sql
CREATE TABLE products (
    id INT PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    type VARCHAR(50) NOT NULL
);

CREATE TABLE product_attributes (
    product_id INT,
    attribute_name VARCHAR(50),
    attribute_value VARCHAR(255),
    FOREIGN KEY (product_id) REFERENCES products(id)
);
```

## 結論

多様な属性を持つ製品のためのデータベーススキーマを設計することは難しい課題であり、アプリケーションの特定の要件を慎重に考慮する必要があります。データをクエリし、管理する方法に応じて、最適なアプローチを選択できます。各方法の長所と短所を理解することで、ニーズに最も適し、アプリケーションが柔軟で効率的に動作するようにするアプローチを選択できます。

## 参考

https://stackoverflow.com/questions/695752/how-to-design-a-product-table-for-many-kinds-of-product-where-each-product-has-m