EAV-すべてのシナリオで本当に悪いですか?


65

私は、プロジェクトのいくつかの要素にエンティティー属性値(EAV)モデルを使用することを考えていますが、Stack Overflowでのそれに関するすべての質問は、 EAVをアンチパターンと呼ぶ答えになります。

しかし、私はそれがすべての場合においてそれが間違っているかどうか疑問に思っています。

ショップ製品のエンティティを考えてみましょう。名前、説明、画像、価格など、ロジックに多くの場所で参加する共通の機能があり、時計やビーチボールなどの(半)固有の機能はまったく異なる側面で説明されます。したがって、EAVはそれらの(半)固有の機能を格納するのに適していると思います。

これはすべて、製品リストを表示するために製品テーブルに十分な情報があり(EAVが関与しないことを意味します)、1つの製品を表示するとき/最大5つの製品などを比較するときだけです。EAVを使用して保存されたデータが使用されます。

Magentoコマースでそのようなアプローチを見てきましたが、非常に人気がありますが、EAVが妥当な場合はありますか?



EAVパターンが非常にうまく機能している例については、Datomicデータベースをご覧ください。すべてをEAVTパターンで保存します(Tは「タイムスタンプ」であり、実際はトランザクションIDに似ています)。彼らの[インデックス作成ドキュメント](docs.datomic.com/indexes.html)はそれを最もよく見せているようです。EAVが非常にうまく機能する例については、Wordpressをご覧ください。
ダン・ロス

回答:


81

https://web.archive.org/web/20140831134758/http://www.dbforums.com/database-concepts-design/1619660-otlt-eav-design-why-do-people-hate.html

EAVは、開発者が必要に応じてスキーマを定義する柔軟性を提供します。これは状況によっては適切です。

一方、クエリの定義が不適切な場合はパフォーマンスが非常に低く、他の不適切なプラクティスをサポートできます。

言い換えれば、EAVはあなたに首を吊るのに十分なロープを与えてくれます。この業界では、プロジェクトであなたに取って代わる人はおそらくばかになるので、物事は最も低いレベルの複雑さに設計されるべきです。


32
最後の文が大好きです。
ゾハーペレド

2
腐ったリンク。キャッシュされたバージョンはどこかにありますか?
ワイルドカード

1
リンクをたどらないでください。ページの読み込みが遅く、役に立たない。また、そのような古いスタイルのフォーラムは悪臭を放ちます。代わりにスタックオーバーフローを使用してください!良い/役に立つ答えに投票して、ゴミ箱を押し下げてください。
ジェス

29

簡単に言えば、EAVは、属性のリストが頻繁に増加する場合、またはすべての属性を列にした場合にほとんどの行がほとんどNULLで埋められるほど大きい場合に役立ちます。そのコンテキスト外で使用すると、アンチパターンになります。


16
「頻繁に」を「実行時に変更する可能性が必要」に置き換えます。
Doc Brown 14年

3
よく知られている「動的」という言葉を使用して、Doc Brownをさらに短縮できます。EAVは、属性のリストが動的に変更される可能性がある場合に役立ちます。
アレクサンダーミルズ

さらに、「属性が変更される可能性がある」場合-「このコンテキストでは「動的に」は少し冗長です:)
Wranorn

1
たとえば、属性を変更するためのフォームにCREATE TABLE新しい属性を実行させるよりも、必然的に便利ですか?
ダミアンジェリック

@DamianYerrick興味深いアプローチ。本番環境でこれを使用しましたか?
掘り出し

21

ショップ製品のエンティティを考えてみましょう。名前、説明、画像、価格など、ロジックの多くの場所に参加する共通の機能があり、時計やビーチボールはまったく異なる側面で説明される(半)固有の機能があります。EAVはそれらの(半)固有の機能を保存するのに適していると思いますか?

EAV構造を使用すると、トレードオフとなるいくつかの影響があります。

null「より複雑なクエリとモデル」に対して「100の列がない」ため、「行のスペースが少なくなります」。

EAVがあるということは、通常、値が任意のデータを挿入できる文字列であることを意味します。これは、有効性と制約チェックに影響を及ぼします。EAVテーブルに何かとして使用するバッテリーの数を入れた状況を考えてみましょう。Cサイズのバッテリーを使用する懐中電灯を探しますが、そのうち4つ未満です。

select P.sku
from
  products P
  attrib Ab on (P.sku = Ab.sku and Ab.key = "batteries")
  attrib Ac on (P.sku = Ac.sku and Ac.key = "count")
where
  cast(Ac.value as int) < 4
  and Ab.value = 'C'
  ...

ここで気づくのは、値に対して合理的にインデックスを使用できないことです。また、値列はさまざまな目的で何度も使用されるため、誰かがそこに整数ではないものや無効な整数(「-1」バッテリーを使用)を入れないようにすることはできません。

これは、製品のモデルを作成しようとすることに影響を及ぼします。あなたは素敵な型付けされた値を持っているでしょう...しかし、あなたはMap<String,String>そこにあらゆる種類のものをそこにただ座っていることになるでしょう。これは、XMLまたはJsonにシリアル化するときにさらに意味を持ち、それらの構造に対して検証またはクエリを実行しようとする複雑さを伴います。

考慮すべきパターンのいくつかの代替または変更は、有効なキーを持つ別のテーブルを持つための自由形式キーの代わりです。これは、データベースで文字列を比較する代わりに、外部キーIDが等しいかどうかをチェックしていることを意味します。キー自体の変更は、1つの場所で行われます。既知のキーのセットがあるため、enumとして実行できます。

特定の製品クラスの属性を含む関連テーブルを作成することもできます。食料品部門には、建築資材に必要のない(およびその逆の)属性が関連付けられた別のテーブルがあります。

+----------+    +--------+    +---------+
|Grocery   |    |Product |    |BuildMat |
|id (fk)   +--->|id (pk) |<---+id (fk)  |
|expiration|    |desc    |    |material |
|...       |    |img     |    |...      |
+----------+    |price   |    +---------+
                |...     |               
                +--------+               

特に EAVテーブルが必要な場合があります。

会社の在庫システムを書いているだけではなく、すべての製品と属性を知っている状況を考えてください。これで、他の会社に販売する在庫システムを作成しています。すべての製品のすべての属性を知ることはできません -それらを定義する必要があります。

出てくる一つの考え方は、もはやどこで何を知っているので、あなたは彼らができる、テーブル構造のメタプログラミングに入るん(「私たちは、顧客がテーブルを変更してもらおう」と、これは単に悪いです丁重台無し構造を、または破損しアプリケーション、彼らは間違ったことをするためのアクセス権を持っているし、そのアクセスの意味が重要になります)。MVC4でこのパスについて詳しく説明しています。実行時にモデルを作成する方法は?

代わりに、EAVテーブルへの管理インターフェイスを作成し、それを使用できるようにします。顧客が「polkadots」のエントリを作成する場合、EAVテーブルに移動し、その対処方法を既に知っています。

この例はRedmineデータベースモデルで見ることができます。custom_fieldsテーブルとcustom_valuesテーブルを見ることができます。これらはシステムを拡張できるEAVの一部です。


テーブル構造全体がリレーショナルではなくEAVのように見える場合は、NoSQLKVフレーバー(cassandra、redis、Mongoなど)を調べてください。これらの設計には、使用目的に適している場合と適切でない場合がある他のトレードオフがしばしば伴うことを認識してください。ただし、これらはEAV構造の意図を持って特別に設計されています。

在庫管理システムのSQLとNoSQLを読むことをお勧めします。

ドキュメント指向のNoSQLデータベース(カウチ、mongo)を使用したこのアプローチに従って、各インベントリアイテムをディスク上のドキュメントと見なすことができます。1つのドキュメント内のすべてをすばやく取得できます。さらに、ドキュメントは構造化されているため、1つのものをすばやく取り出すことができます。一方、特定の属性に一致するものをすべてのドキュメントで検索すると、パフォーマンスが低下する可能性があります(すべてのファイルに対して 'grep'を使用して比較してください)...すべてのトレードオフです。

別のアプローチとしては、LDAPがあります。LDAPでは、関連するすべてのアイテムを含むベースがありますが、他のタイプのアイテムには追加のオブジェクトクラスが適用されます。(LDAPを使用したシステムインベントリを参照)

このパスをたどると、探しているものと完全に一致するもの見つかるかもしれません...すべてにいくつかのトレードオフがあります。


10

6年後

今というPostgresのでJSONがここにあり、私たちはPostgresのを使っている人のために、別のオプションを持っています。製品にいくつかの追加データのみを添付する場合、ニーズは非常に簡単です。例:

CREATE TABLE products (sku VARCHAR(30), shipping_weight REAL, detail JSON);
INSERT INTO products ('beachball', 1.0, '{"colors": ["red", "white"], "diameter": "50cm"}');

SELECT * FROM products;
    sku    | weight |               detail               
-----------+--------+------------------------------------
 beachball |      1 | {"colors": ["red", "white"], "diameter": "50cm"}

:ここではPostgresの中にJSONへのスムーズな導入だhttps://www.compose.com/articles/is-postgresql-your-next-json-database/が

Postgresは、プレーンテキストJSONではなくJSONBを実際に保存し、JSONBドキュメント/フィールド内のフィールドのインデックスをサポートしていることに注意してください。実際にそのデータに対してクエリを実行する必要がある場合です。

また、JSONBフィールド内のフィールドは、UPDATEクエリで個別に変更できないことに注意してください。JSONBフィールドのコンテンツ全体を置き換える必要があります。

この回答は質問に直接対処するものではないかもしれませんが、EAVパターンに代わるものを提供します。これは元の質問を熟考している人なら誰でも考慮すべきです。


3
代替ソリューションを投稿するのは素晴らしいアイデアだと思います。他の人を順調に保つために、MS SQLはしばらくの間それらをインデックス化する機能を備えたXML列をサポートしていました。 )。一方、私が読んだことから、Postgres JSONのサポートは優れています。たとえば、JSON配列プロパティのデータのインデックスをサポートしているようです。
ジードリウス

1
「... JSONBフィールド内のフィールドをUPDATEクエリで個別に変更することはできません。JSONBフィールドのコンテンツ全体を置き換える必要があります。」 これは時代遅れですよね?jsonb_set()Postgres 9.5以降には、まさにこれのための関数があります。(リンクにリンクした記事は、9.5の機能追加について説明する新しい記事に順番にリンクします。)
ワイルドカード

7

通常、ルックアップテーブルに使用している場合や、1つまたは2つの保存された値のテーブルを作成する必要がないという利点がある他の状況では、人々は別の見方をします。あなたが説明している状況、つまり基本的にアイテムのプロパティを保存している状況は、完全に正常な(そして正規化された)ように聞こえます。可変数のアイテム属性を格納するためにテーブルを広げることは悪い考えです。

異なるデータを長い細いテーブルに保存する一般的な場合... 必要に応じて新しいテーブルを作成することを恐れてはいけません。 2つの短いファットテーブル。

そうは言っても、ロギングにEAVテーブルを使用することで有名です。彼らはいくつかの良いユーティリティを持っています。


「スキニーテーブル」と「ファットテーブル」を定義してください。
Tulainsコルドバ

@TulainsCórdova:「スキニー」テーブルは、行数と列数が少ないテーブルになりますが、ファットテーブルは、列数と行数が多いテーブルになります。一例として、本などのプロパティがあるルックアップテーブルを作成します。ファットテーブルには、ブックご​​とに1つのレコードがあり、特定のデータ用に多くの列がありますが、シンテーブルには、4つの列id、book、field_name、field_dataがあります。最初の方法の利点は、レコード数が少ないことですが、マイナス面は、一部のフィールドが空白になる可能性があり、全体を拡張するのが難しいことです。
悪魔のような子犬

@Satanicpuppyあなたのスキニー/脂肪の定義は混同されていると思います-それらは同じです。スキニーテーブルには列と行がほとんどないということですか?
チャールズウッド

1

EAVは、明示的な構造の問題を暗黙の知覚に変更します。Xは列AとBを持つテーブルであると言うよりも、列AとBがテーブルXを形成することを意味します。ある意味では逆ですが、必ずしも1対1のマッピングはありません。AとBは両方ともテーブルX(またはタイプ)にマッピングされると言えます。これは、コンテキストが重要な、より複雑なドメインで重要になる可能性があります。

私はこのタイプのアプローチのためにDatomicを研究してきましたが、Datomicを使って何をすべきか(不可能ではない)を制限した非常に便利で強力なシステムだと思います。

EAVが遅いこと、または「首を吊るのに十分なロープを与える」ことは、私が同意する声明ではありません。むしろ、EAVの長所に重点を置き、問題のスペースに合っている場合は、検討する必要があります。

私の経験では、それはモデリングに対する素晴らしいほぼ制約のないアプローチです。特に、Datomicの場合、それらはすべての上に集合セマンティックを課します。リレーションシップをモデル化するモデリングの決定は、列/テーブルを再設計する必要なく、自由に1つから多数に変更できます。制約が不変式に違反しない限り、戻ることもできます。それはボンネットの下ですべて同じです。

EAVの問題は、Datomicのような実装の欠如にあります。これはEAVについての質問なので、Datomicについて絶賛したくはありませんが、EAVに関してすべてが正しいと思うものの1つです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.