📌

SQL 同じ意味を持つ値は、1つの列に格納する

2025/01/20に公開

はじめに

最近、オライリー・ジャパンの「SQLアンチパターン」という本を読んでいます。前回の記事に引き続き、今回は「マルチカラムアトリビュート(複数列属性)」というアンチパターンについて、私なりの実例を交えながら解説したいと思います。

マルチカラムアトリビュート(複数列属性)とは?

データベース設計において、同じような意味を持つデータを複数の列に分けて格納するケースがあるようです。このような設計パターンは「マルチカラムアトリビュート(複数列属性)」と呼ばれ、SQLアンチパターンの一つとして知られています。

参考著書

オライリー・ジャパン「SQLアンチパターン」
https://www.oreilly.co.jp/books/9784873115894/

問題のある実装例

以下は、ユーザーの電話番号を格納するテーブルの例です。

CREATE TABLE users (
    user_id INT PRIMARY KEY,
    name VARCHAR(100),
    phone1 VARCHAR(20),
    phone2 VARCHAR(20),
    phone3 VARCHAR(20)
);

このような設計では、各ユーザーに対して最大3つの電話番号を格納できます。一見便利そうに見えますが、実際には多くの問題を引き起こす可能性があります。

デメリット

  1. 拡張性の制限

    • 電話番号が4つ以上必要になった場合、テーブル構造の変更が必要
    • ALTER TABLE文の実行は、大規模なデータベースでは重い操作となる
  2. 検索の複雑化

    • 特定の電話番号を検索する際、すべての電話番号列を確認する必要がある
    SELECT * FROM users 
    WHERE phone1 = '090-1234-5678' 
       OR phone2 = '090-1234-5678' 
       OR phone3 = '090-1234-5678';
    
  3. データの正規化違反

    • 同じ種類のデータが複数の列に分散
    • データの一貫性維持が困難
  4. NULL値の増加

    • 電話番号が1つしかない場合でも、未使用の列にNULLが入る

解決策

より適切な設計は、以下のように別テーブルを作成することです。

CREATE TABLE users (
    user_id INT PRIMARY KEY,
    name VARCHAR(100)
);

CREATE TABLE phone_numbers (
    id INT PRIMARY KEY,
    user_id INT,
    phone_number VARCHAR(20),
    FOREIGN KEY (user_id) REFERENCES users(user_id)
);

このような設計には以下のメリットがあります。

  1. 柔軟な拡張性

    • 電話番号の数に制限がない
    • テーブル構造の変更なしで対応可能
  2. シンプルな検索

    SELECT u.* FROM users u
    JOIN phone_numbers p ON u.user_id = p.user_id
    WHERE p.phone_number = '090-1234-5678';
    
  3. データの正規化

    • 電話番号データが1つの列にまとめられる
    • データの一貫性が保たれやすい

まとめ

マルチカラムアトリビュートは、一見便利そうに見えますが、長期的には多くの問題を引き起こす可能性があることがわかりました。

代わりに、関連テーブルを作成し、1つの列に同じ種類のデータをまとめて格納する設計を採用することで、より柔軟でメンテナンス性の高いデータベースを実現できることを学びました。

データベース設計において、「同じ意味を持つデータは1つの列に格納する」という原則を守ることで、将来的な拡張性やメンテナンス性を確保することができますね。

Discussion