 
                      ユーザーの複数の保有資格のデータを扱う
こんなブログを読んでいる方はSEとかプログラマが多いのだと思うのだけど、ユーザー管理のプログラムを作っているときなど、こんな要望を受けたことはないだろうか?
「ユーザーの保有している資格を管理して欲しい。資格5種類あって、ユーザーはいくつかの資格を保有する予定。」
タイプA
単純にプログラムするならユーザーテーブルに資格A、資格Bと、持つという方法が一般的。
ユーザーマスタ
| ユーザid | 氏名 | … | 資格A | 資格B | 資格C | 資格D | 資格E | 
|---|---|---|---|---|---|---|---|
| 1 | 山田 太郎 | … | true | false | true | false | false | 
| 2 | 佐藤 一郎 | … | true | false | false | true | true | 
ところがである。あるとき、「資格の個数が増る」だよね。って言われることがある。そうすると、途端にプログラムを書き換えないとならなくなります。
タイプB
こういうときは、ちゃんと資格をマスターテーブルにしてという方法があります。
ユーザーマスタ
| ユーザid | 氏名 | … | 資格1 | 資格2 | 資格3 | 資格4 | 資格5 | 
|---|---|---|---|---|---|---|---|
| 1 | 山田 太郎 | … | 1 | 3 | 8 | ||
| 2 | 佐藤 一郎 | … | 1 | 4 | 5 | 
資格マスタ
| 資格id | 名称 | 
|---|---|
| 1 | 資格A | 
| 2 | 資格A | 
| … | … | 
| 8 | 資格H | 
| 9 | 資格I | 
ところがこの方法には、弱点もあります。まず、一人当たりの保有する資格が増えるとテーブルの構造を変更しないとならなくこと。もうひとつは、資格を保有しているユーザーを検索する方法が面倒になることです。
たとえば、資格Iを保有しているユーザーを検索するには、ユーザーの資格欄ごとに検索しないとなりません。
SELECT * FROM [ユーザーマスタ]
WHERE [資格1] = 9 OR [資格2] = 9 OR [資格3] = 9 OR [資格4] = 9 OR [資格5] = 9 OR [資格2] = 9 
タイプC
こんなときに取られる方法が、もっとも汎用性の高い、ユーザーマスタ、資格マスタ、ユーザー保有資格テーブルの3つ完全に分離する方法です。
ユーザーマスタ
| ユーザid | 氏名 | … | 
|---|---|---|
| 1 | 山田 太郎 | … | 
| 2 | 佐藤 一郎 | … | 
資格マスタ(タイプBと同じ)
ユーザー保有資格テーブル
| ユーザid | 資格ID | 
|---|---|
| 1 | 1 | 
| 1 | 3 | 
| … | … | 
この方法のメリットは、なんと言っても拡張性と柔軟性があることです。
SELECT U.* FROM [ユーザーマスタ] AS U
JOIN [ユーザー保有資格テーブル] AS C ON U.ユーザid = C.ユーザid
WHERE C.資格ID = 9
この方法はデータベースから見ると完璧な方法なのですが、プログラムするほうから見ると、ユーザーマスタを編集すると一緒にユーザー保有資格テーブルも編集しないとなりません。結構面倒なことになります。ユーザーマスタを削除すると一緒に、ユーザー保有資格テーブルも削除しないと不整合が生じたりと、プログラムの作成には注意が必要になります。
タイプD
最後にちょっとデータベース的にはトリッキーだけど、ちょっと変わった方法を説明します。
タイプBでは、それぞれのユーザーが持つ資格を整数の項目にしましたが、この方法では文字列にします。保有している資格を文字列の形で適当な記号をセパレータにして記録します。ここでは、|を記号にしました。
ユーザーマスタ
| ユーザid | 氏名 | … | 資格 | 
|---|---|---|---|
| 1 | 山田 太郎 | … | |1|3|8| | 
| 2 | 佐藤 一郎 | … | |1|4|5| | 
前後も同じ記号をつけることで検索もこんな風になります。タイプCに比べ、検索のコスト的には高くなりますが、ユーザー件数が100万件とかと膨大にならない限り十分に対応できます。
SELECT * FROM [ユーザーマスタ] WHERE [資格] LIKE ‘|3|’
タイプE
最後に、PostgreSQLで導入されたArray型を使う方法を説明します。資格を整数のArray型にすることで、簡単に表すことができます。
ユーザーマスタ
| ユーザid | 氏名 | … | 資格 | 
|---|---|---|---|
| 1 | 山田 太郎 | … | 1,3,8 | 
| 2 | 佐藤 一郎 | … | 1,4,5 | 
SELECT * FROM [ユーザーマスタ] WHERE 3 IN [資格]
■まとめ
| タイプ | メリット | デメリット | 
|---|---|---|
| タイプA | 単純でデータの扱いも簡単 | 拡張性が無く、拡張があると改修が必要 | 
| タイプB | 拡張性はまずまず。 | 検索などのプログラムなどが面倒。一人当たりの保有資格数を増やすと改修が必要。速度的には少々疑問。 | 
| タイプC | 将来的な拡張性がある。スタッフ、資格ともに多くなっても対応可能 | プログラムの作成が大変。データの取り扱いに注意を要する。 | 
| タイプD | 将来的な拡張性が高い。速度はまずまず。 | データベース的にはトリッキーな方法です。 | 
| タイプE | 将来的な拡張性、データ取扱も簡単。 | 対応するデータベースが少ない | 
こういう将来に渡って使われるデータでは、将来性と開発の容易さを勘案してデータ構造を決めないとあとで大変な思いをすることになります。
yna