使い慣れたフォーム(エンティティごとに1行、属性ごとに1列)から見慣れないフォーム(属性ごとにエンティティごとに1行)にデータベースをリファクタリングしたクライアントからのルーチンデータフィードを処理します。
前:属性ごとに1列
ID Ht_cm wt_kg Age_yr ...
1 190 82 43 ...
2 170 60 22 ...
3 205 90 51 ...
後:すべての属性に1列
ID Metric Value
1 Ht_cm 190
1 Wt_kg 82
1 Age_yr 43
1 ...
2 Ht_cm 170
2 Wt_kg 60
2 Age_yr 22
2 ...
3 Ht_cm 205
3 Wt_kg 90
3 Age_yr 51
3 ...
このデータベース構造に名前はありますか?相対的な利点は何ですか?古い方法では、特定の属性(null以外、negative以外など)に有効性制約を設定する方が簡単で、平均の計算も簡単です。しかし、データベースをリファクタリングせずに新しい属性を追加する方が簡単な場合があることがわかります。これはデータを構造化する標準的な方法ですか?
これはEntity-Attribute-Value(「名前と値のペア」と呼ばれることもあります)と呼ばれ、リレーショナルデータベースでEAVパターンを使用する場合の「四角い穴の丸いペグ」の典型的なケースです。
あなたがEAVを使用してはならない理由のリストは次のとおりです:
SELECT height, weight, age FROM Client where height is null or weight is null
よりもはるかに難しいことです。比較:
SELECT cID.ID AS [ID], cH.Value AS [Height], cW.Value AS [Weight], cA.Value AS [Age]
FROM (SELECT DISTINCT ID FROM Client) cID
LEFT OUTER JOIN
Client cW ON cID.ID = cW.ID AND cW.Metric = "Wt_kg"
LEFT OUTER JOIN
Client cH ON cID.ID = cH.ID AND cW.Metric = "Ht_cm"
LEFT OUTER JOIN
Client cA ON cID.ID = cA.ID AND cW.Metric = "Age_yr"
に:
SELECT c.ID, c.Ht_cm, c.Wt_kg, c.Age_yr
FROM Client c
EAVを使用する必要がある場合の(非常に短い)リストは次のとおりです:
私がこの投稿全体を費やして、EAVがほとんどの場合ひどい考えである理由を詳しく説明したことはわかっていますが、それが必要/不可避な場合がいくつかあります。ただし、ほとんどの場合(上記の例を含む)、それは価値があるよりもはるかに面倒になります。 EAVタイプのデータ入力を幅広くサポートする必要がある場合は、Key-Valueシステムにそれらを格納することを検討する必要があります。 Hadoop/HBase、CouchDB、MongoDB、Cassandra、BerkeleyDB。
PostgreSQLでは、EAV構造を処理する非常に良い方法の1つは、追加モジュール hstore
です。バージョン8.4以降。私はマニュアルを引用します:
このモジュールは、単一のPostgreSQL値内にキーと値のペアのセットを格納するための
hstore
データ型を実装します。これは、ほとんど検査されない多くの属性を持つ行や半構造化データなど、さまざまなシナリオで役立ちます。キーと値は単なるテキスト文字列です。
Postgres 9.2以降、 json
タイプとそれに伴う機能のホスト(- ほとんどは9.3で追加されました )。
Postgres 9.4は(大いに優れています!)「バイナリJSON」データ型 jsonb
のリストに追加しますオプション。高度なインデックスオプション。
EAV dbモデルがどのように批判され、「アンチパターン」と見なされているかを見るのはおかしいです。
私に関する限り、主な欠点は次のとおりです。
ただし、このソリューションは絶対に破棄しないでください。理由は次のとおりです。
EAV構造を使用しているデータベースがある場合、さまざまな方法でデータを照会できます。
@ Simonの回答 は、複数の結合を使用してクエリを実行する方法をすでに示しています。
使用されるサンプルデータ:
CREATE TABLE yourtable ([ID] int, [Metric] varchar(6), [Value] int);
INSERT INTO yourtable ([ID], [Metric], [Value])
VALUES (1, 'Ht_cm', 190),
(1, 'Wt_kg', 82),
(1, 'Age_yr', 43),
(2, 'Ht_cm', 170),
(2, 'Wt_kg', 60),
(2, 'Age_yr', 22),
(3, 'Ht_cm', 205),
(3, 'Wt_kg', 90),
(3, 'Age_yr', 51);
PIVOT
関数( SQL Server 2005 + / Oracle 11g + )を持つRDBMSを使用している場合は、次の方法でデータをクエリできます。
select id, Ht_cm, Wt_kg, Age_yr
from
(
select id, metric, value
from yourtable
) src
pivot
(
max(value)
for metric in (Ht_cm, Wt_kg, Age_yr)
) piv;
PIVOT
関数にアクセスできない場合は、CASE
ステートメントで集計関数を使用してデータを返すことができます。
select id,
max(case when metric ='Ht_cm' then value else null end) Ht_cm,
max(case when metric ='Wt_kg' then value else null end) Wt_kg,
max(case when metric ='Age_yr' then value else null end) Age_yr
from yourtable
group by id
これらのクエリはどちらも結果にデータを返します。
| ID | HT_CM | WT_KG | AGE_YR |
-------------------------------
| 1 | 190 | 82 | 43 |
| 2 | 170 | 60 | 22 |
| 3 | 205 | 90 | 51 |