キー値のこのデータベーススキーマの名前はありますか?


68

慣れていると思われる形式(エンティティごとに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、非負など)に有効性制約を設定するのが簡単で、平均を計算するのが簡単です。ただし、データベースをリファクタリングせずに新しい属性を追加する方が簡単な場合があります。これは、データを構造化するための標準的な方法ですか?

回答:


91

これはEntity-Attribute-Value(「名前と値のペア」とも呼ばれます)と呼ばれ、リレーショナルデータベースでEAVパターンを使用する場合の「四角い穴の丸釘」の典型的なケースです。

EAVを使用しない理由のリストを以下に示します。

  • データ型を使用することはできません。値が日付、数値、またはお金(10進数)のいずれでもかまいません。常にvarcharに強制されます。これは、軽微なパフォーマンスの問題から大胆な痛みまで、何でもかまいません(毎月のロールアップレポートで1セントの変動を追跡する必要がありましたか?)。
  • 制約を(簡単に)強制することはできません。「すべての人が0〜3メートルの高さを必要とする」または「年齢がnullでなく、0以上でなければならない」ことを強制するには、これらの各制約が1〜2行ではなく、適切にモデル化されたシステムで。
  • 上記に関連して、各クライアントに必要な情報を取得することを簡単に保証することはできません(年齢が不足している可能性があり、次に次の高さが不足している可能性があるなど)。あなたはそれ行うことができますが、それは地獄よりもはるかに困難ですSELECT height, weight, age FROM Client where height is null or weight is null
  • 繰り返しになりますが、重複データの検出ははるかに困難です(1つのクライアントで2つの年齢を指定するとどうなりますか?1つの属性が2倍になった場合、以下のようにデータのEAVを解除すると2行の結果が得られます。 2つの属性に対して2つの個別のエントリがある場合、以下のクエリから4行を取得します)。
  • 属性名の一貫性を保証することさえできません。「Age_yr」は「AGE_IN_YEARS」または「age」になる場合があります。(確かに、データを挿入しているときよりも抽出物を受け取っているときの方が問題は少ないですが、それでもなお。)
  • あらゆる種類の非自明なクエリは、完全な災害です。合理的な方法でクエリを実行できるように3属性のEAVシステムをリレーショナル化するには、EAVテーブルの3つの結合が必要です。

比較する:

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タイプのデータ入力を幅広くサポートする必要がある場合は、Hadoop / HBase、CouchDB、MongoDB、Cassandra、BerkeleyDBなどのキーバリューシステムにデータを保存することを検討する必要があります。


7
マイナーな通知を伴う+1:異なるタイプの値を異なるテーブルに入れると、データタイプを使用できます(まあ、古典的なEAVではなく、一種の改善)。(しかし、追加の質問があります:新しい属性のタイプをどのように知っていますか?)
dezso

4
同意しましたが、システムに意味的に関係のないもの(スキーマレスだけでなく)のリストを保持している場合、EAVも使用するのに適したアプローチであると付け加えます。たとえば、製品の機能を保存および一覧表示する必要があるオンライン製品カタログ。逆流するキー/値のペアのリストがありますが、システムは実際にはそれらのキーまたは値が何であるかを知りません。そのような状況では、EAVの危険は無関係です。
ジョエルブラウン

10
@JoelBrown気にしないが、道中にVPがカタログにあるシャツに茶色のボタンとボタンダウンの襟の両方があるシャツの数を尋ねる場合は、書くのが面倒だ。EAV自体は通常、計画または先見性の欠如を示しています。
JNK

2
@JoelBrown私はそれが(非常に小さい非常に狭い)用途を持っていることに異論はありません。情報がこれまで任意の構造化された方法で照会される可能性がある場合しかし、それはおそらく、EAVにすべきではない
JNK

4
@JoelBrownビジネス要件または保存しているデータが変更された場合、データモデルも変更する必要があります。データモデルを石で彫ってはいけません。また、リレーショナルデータベースの場合、人々がEAVを使用する時間の99%は、「私の知っているすべてのデータベースパターンとモデルを考慮するのではなく、「データの保存方法を考えるのに時間を費やしたくない」 EAVはこのデータセットに最適です」。繰り返します、EAVが有用な場合もあります(おそらく「正しい」答えでさえあります)が、それらはほとんどありません。
サイモン・リガーツ

18

エンティティ属性値(EAV)

私を含め、多くの人がアンチパターンであると考えています。

代替手段は次のとおりです。

  1. データベーステーブルの継承を使用する

  2. XMLデータとSQLXML関数を使用する

  3. HBaseのようなnosqlデータベースを使用する


3
間違いなくほとんどのユースケースのアンチパターン。本当に小さなデータセットがあり、パフォーマンスが問題にならない場合、それはあなたのために働くかもしれません。
JNK

16

PostgreSQLでは、EAV構造を扱う非常に良い方法の1つは、hstoreバージョン8.4以降で使用可能な追加モジュールです。私はマニュアルを引用します:

このモジュールhstoreは、単一のPostgreSQL値内にキー/値ペアのセットを保存するためのデータ型を実装します。これは、めったに検査されない多くの属性を持つ行や、半構造化されたデータなど、さまざまなシナリオで役立ちます。キーと値は単なるテキスト文字列です。

Postgres 9.2以降、それに付随するjsonタイプと機能のホストもあります(ほとんどが9.3で追加されました)。

Postgres 9.4は、jsonbオプションリストに(ほぼ優れた!)「バイナリJSON」データタイプを追加します。高度なインデックスオプション。


10

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;

SQL Fiddle with Demoをご覧ください

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

SQL Fiddle with Demoをご覧ください

これらのクエリはどちらも結果にデータを返します。

| ID | HT_CM | WT_KG | AGE_YR |
-------------------------------
|  1 |   190 |    82 |     43 |
|  2 |   170 |    60 |     22 |
|  3 |   205 |    90 |     51 |

10

EAV dbモデルがどのように批判され、「アンチパターン」と見なされているかを見るのは面白いです。

私に関する限り、主な欠点は次のとおりです。

  • 少し前に既にEAVの使用を開始したプロジェクトに参加する場合、学習曲線はより急です。実際、結合(およびテーブル)の数を大幅に増やすため、クエリは難しいため、理解するためにより多くの時間が必要になります。Magentoプロジェクトを見て、プロジェクトの外部の開発者がどのようにDBで作業するのに苦労するかを確認してください。
  • 「M」などで始まる名前の人の数を取得する必要がある場合、レポートは適していません ...

ただし、このソリューションを絶対に破棄しないでください。その理由は次のとおりです。

  • サイモンは、「要件の変更」と呼ばれるモンスターについて話しました。私はこの表現が好きです:)。そして、これがまさにEAVが良い候補になる理由です。なぜなら、これは"change"に非常に適しているからです。もちろん、変更する要件によって異なります。もちろん、まったく新しいビジネスについて話している場合は、dataModelを確認する必要がありますが、EAVには多くの柔軟性があります。厳密さを求めるからといって、これがそれほど面白くないというわけではありません。
  • また、「データ型は使用できない」とも言われていました。:これは間違っています。各dataTypeに1つずつ、複数の値テーブルがあります。次に、どの種類のdataTypeが属性であるかを属性テーブルで指定する必要があります。実際、古典的なリレーショナル/ EAVとクラスリレーションシップの組み合わせは、データベース設計で多くの興味深い可能性を提供します。

2
学習曲線は、遭遇する最初のEAV設計にとって急勾配です。その後、すべてが似ています。
ypercubeᵀᴹ

1
一時的なコメント:「報告に適していない」と主張する理由がわかりません。EAVはレポートに最適です。eav.valuesからObjectIdを選択します。propertyId= 'm%'のような名前と値です。仮想スキーマの変更(プロパティの追加など)は、再コンパイルすることなく、動的なレポートインターフェイス(ドロップダウンなど)に含めることができます。
crokusek
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.