EAV - apakah benar-benar buruk di semua skenario?

65

Saya berpikir untuk menggunakan model entitas-atribut-nilai (EAV) untuk beberapa hal di salah satu proyek, tetapi semua pertanyaan tentang hal itu di Stack Overflow berakhir dengan jawaban yang menyebut EAV sebagai anti-pola.

Tapi saya bertanya-tanya apakah itu salah dalam semua kasus.

Katakanlah entitas produk toko, ia memiliki fitur umum, seperti nama, deskripsi, gambar, dan harga, yang mengambil bagian dalam banyak tempat logika dan memiliki (semi) fitur unik, seperti arloji dan bola pantai akan dijelaskan oleh aspek yang sama sekali berbeda. Jadi saya pikir EAV akan cocok untuk menyimpan fitur-fitur unik (semi).

Semua ini dengan asumsi, bahwa untuk menampilkan daftar produk, cukup info di tabel produk (itu berarti tidak ada EAV yang terlibat) dan tepat ketika menunjukkan satu produk / membandingkan hingga 5 produk / dll. data yang disimpan menggunakan EAV digunakan.

Saya telah melihat pendekatan semacam itu dalam perdagangan Magento dan ini cukup populer, jadi apakah ada kasus ketika EAV masuk akal?

Giedrius
sumber
2
@busy_wait tabel "Entity-Attibute-Value" - lihat model Entity – atribut-value di Wikipedia .
Ross Patterson
Untuk contoh pola EAV yang bekerja dengan sangat baik, lihatlah pada basis data Datomic. Ini menyimpan segala sesuatu dalam pola EAVT (T adalah "timestamp", sebenarnya lebih mirip id transaksi). [Dokumentasi pengindeksan] mereka (docs.datomic.com/indexes.html) tampaknya menunjukkan yang terbaik. Untuk contoh EAV yang bekerja sangat buruk, lihat Wordpress .
Dan Ross

Jawaban:

81

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

EAV memberikan fleksibilitas kepada pengembang untuk menentukan skema yang diperlukan dan ini bagus dalam beberapa keadaan.

Di sisi lain, kinerjanya sangat buruk dalam hal kueri yang tidak jelas dan dapat mendukung praktik buruk lainnya.

Dengan kata lain, EAV memberi Anda cukup banyak tali untuk menggantung diri dan dalam industri ini, segala sesuatunya harus dirancang dengan tingkat kerumitan terendah karena orang yang menggantikan Anda dalam proyek itu kemungkinan besar akan menjadi idiot.

maple_shaft
sumber
32
Cinta kalimat terakhir.
Zohar Peled
2
Tautan busuk. Apakah ada versi cache di suatu tempat?
Wildcard
1
Jangan ikuti tautannya. Halaman dimuat dengan lambat dan tidak membantu. Juga, forum gaya lama seperti itu bau. Gunakan stack overflow! Mengunggah jawaban yang baik / membantu dan menekan sampah.
Jess
29

Singkatnya, EAV berguna ketika daftar atribut Anda sering bertambah, atau ketika itu sangat besar sehingga sebagian besar baris akan diisi dengan sebagian besar NULL jika Anda membuat setiap atribut menjadi kolom. Ini menjadi anti-pola ketika digunakan di luar konteks itu.

Karl Bielefeldt
sumber
16
Saya akan mengganti "sering" dengan "membutuhkan kemungkinan untuk diubah pada saat run-time".
Doc Brown
3
Kami dapat mempersingkat bahwa Doc Brown lebih jauh dengan menggunakan kata "dinamis" yang cukup dipahami - EAV berguna ketika daftar atribut Anda dapat berubah secara dinamis.
Alexander Mills
Lebih jauh ke "ketika atribut Anda dapat berubah" - "secara dinamis" agak berlebihan dalam konteks ini :)
Wranorn
1
Apakah ini tentu lebih bermanfaat daripada, katakanlah, memiliki formulir untuk mengubah atribut melakukan CREATE TABLEuntuk atribut baru?
Damian Yerrick
@DamianYerrick, pendekatan yang menarik. Sudahkah Anda menggunakan ini dalam produksi?
gali
21

Katakanlah entitas produk toko, ia memiliki fitur umum, seperti nama, deskripsi, gambar, harga, dll., Yang mengambil bagian dalam logika banyak tempat dan memiliki (semi) fitur unik, seperti arloji dan bola pantai akan dijelaskan oleh aspek yang sama sekali berbeda . Jadi saya pikir EAV cocok untuk menyimpan fitur-fitur unik (semi)?

Menggunakan struktur EAV untuk memiliki beberapa implikasi yang merupakan trade off.

Anda memperdagangkan 'ruang lebih sedikit untuk baris karena Anda tidak memiliki 100 kolom yang null' menentang 'kueri dan model yang lebih rumit'.

Memiliki EAV biasanya berarti nilainya adalah string tempat seseorang dapat memasukkan data apa pun. Ini kemudian memiliki implikasi pada validitas dan pengecekan kendala. Pertimbangkan situasi di mana Anda telah menempatkan jumlah baterai yang digunakan sebagai sesuatu di tabel EAV. Anda ingin menemukan senter yang menggunakan baterai berukuran C, tetapi kurang dari 4 di antaranya.

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'
  ...

Hal yang perlu disadari di sini adalah Anda tidak dapat menggunakan indeks secara wajar pada nilainya. Anda juga tidak dapat mencegah seseorang memasukkan sesuatu yang bukan bilangan bulat di sana, atau bilangan bulat tidak valid (menggunakan baterai '-1') karena kolom nilai digunakan berulang kali untuk tujuan yang berbeda.

Ini kemudian memiliki implikasi dalam mencoba menulis model untuk produk tersebut. Anda akan memiliki nilai yang diketik dengan baik ... tetapi Anda juga akan memiliki Map<String,String>hanya duduk di sana dengan segala macam barang di dalamnya. Hal ini kemudian memiliki implikasi lebih lanjut ketika serialisasi itu ke XML atau JSON dan kompleksitas mencoba untuk melakukan validasi atau query terhadap orang-orang struktur.

Beberapa alternatif atau modifikasi pada pola untuk dipertimbangkan adalah bukan kunci bentuk bebas, untuk memiliki tabel lain dengan kunci yang valid. Itu berarti alih-alih melakukan perbandingan string dalam database, Anda memeriksa terhadap kesetaraan id kunci asing. Mengubah kunci itu sendiri dilakukan di satu tempat. Anda memiliki seperangkat kunci yang dikenal yang berarti bahwa mereka dapat dilakukan sebagai enum.

Anda juga bisa memiliki tabel terkait yang berisi atribut dari kelas produk tertentu. Sebuah departemen kelontong dapat memiliki meja lain yang memiliki beberapa atribut yang terkait dengannya bahwa bahan bangunan tidak perlu (dan sebaliknya).

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

Ada saat-saat yang terutama panggilan untuk meja EAV.

Pertimbangkan situasi di mana Anda tidak hanya menulis sistem inventaris untuk perusahaan Anda di mana Anda mengetahui setiap produk dan setiap atribut. Anda sekarang sedang menulis sistem persediaan untuk dijual ke perusahaan lain. Anda tidak dapat mengetahui setiap atribut dari setiap produk - mereka harus mendefinisikannya.

Satu ide yang keluar adalah "kami akan membiarkan pelanggan memodifikasi tabel" dan ini hanya buruk (Anda masuk ke meta-pemrograman untuk struktur tabel karena Anda tidak lagi tahu di mana, mereka secara meriah dapat merusak struktur atau merusak aplikasi, mereka punya akses untuk melakukan hal-hal yang salah dan implikasi dari akses itu menjadi signifikan). Ada lebih banyak tentang jalur ini di MVC4: Bagaimana cara membuat model saat dijalankan?

Sebagai gantinya, Anda membuat antarmuka administratif ke tabel EAV dan mengizinkannya digunakan. Jika pelanggan ingin membuat entri untuk 'polkadots', ia masuk ke tabel EAV dan Anda sudah tahu cara menghadapinya.

Contohnya dapat dilihat dalam model database untuk Redmine Anda dapat melihat tabel custom_fields, dan tabel custom_values ​​- itu adalah bagian dari EAV yang memungkinkan sistem diperluas.


Perhatikan bahwa jika Anda menemukan seluruh struktur tabel Anda terlihat seperti EAV daripada relasional, Anda mungkin ingin melihat rasa KV dari NoSQL (cassandra, redis, Mongo, ... ...). Sadarilah bahwa ini sering kali disertai pengorbanan lain dalam desain mereka yang mungkin cocok atau tidak sesuai dengan apa yang Anda gunakan. Namun, mereka dirancang khusus dengan maksud struktur EAV.

Anda mungkin ingin membaca SQL vs NoSQL untuk sistem manajemen inventaris

Mengikuti pendekatan ini dengan basis data NoSQL yang berorientasi pada dokumen (sofa, mongo), Anda dapat mempertimbangkan setiap item inventaris sebagai dokumen pada disk ... menarik semuanya dalam satu dokumen dengan cepat. Selanjutnya, dokumen ini disusun sehingga Anda dapat menarik satu hal dengan cepat. Di sisi lain, mencari semua dokumen untuk hal-hal yang cocok dengan atribut tertentu dapat memiliki kinerja yang lebih sedikit (bandingkan dengan menggunakan 'grep' terhadap semua file) ... semuanya merupakan trade off.

Pendekatan lain adalah LDAP di mana seseorang akan memiliki basis dengan semua item yang terkait, tetapi kemudian juga akan memiliki kelas objek tambahan yang diterapkan padanya untuk jenis item lainnya. (lihat Inventarisasi Sistem Menggunakan LDAP )

Setelah Anda menyusuri jalan ini, Anda mungkin menemukan sesuatu yang sama persis dengan apa yang Anda cari ... meskipun semuanya memiliki beberapa pengorbanan.

Komunitas
sumber
10

6 tahun kemudian

Sekarang JSON di Postgres ada di sini, kami memiliki opsi lain, bagi mereka yang menggunakan Postgres. Jika Anda hanya ingin melampirkan beberapa data tambahan ke suatu produk, maka kebutuhan Anda cukup sederhana. Contoh:

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"}

Berikut ini adalah pengantar JSON yang lebih lancar di Postgres: https://www.compose.com/articles/is-postgresql-your-next-json-database/ .

Perhatikan bahwa Postgres sebenarnya menyimpan JSONB, bukan JSON teks biasa, dan ia mendukung indeks pada bidang-bidang di dalam dokumen / bidang JSONB, jika Anda menemukan bahwa Anda sebenarnya ingin melakukan kueri terhadap data tersebut.

Juga, perhatikan bahwa bidang dalam bidang JSONB tidak dapat dimodifikasi secara individual dengan kueri UPDATE; Anda harus mengganti seluruh konten bidang JSONB.

Jawaban ini mungkin tidak secara langsung menjawab pertanyaan, tetapi menawarkan alternatif untuk pola EAV, yang harus dipertimbangkan oleh siapa saja yang merenungkan pertanyaan awal.

Dan Ross
sumber
3
Saya pikir itu ide bagus untuk mengirim solusi alternatif. Hanya untuk menjaga orang lain di jalur, MS SQL mendukung kolom XML dengan kemampuan untuk mengindeks mereka untuk sementara waktu dan mulai tahun 2016 dapat melakukan hal yang sama dengan JSON (walaupun JSON bukan tipe kolom asli dalam MS SQL, Anda masih dapat mengindeksnya ). Di sisi lain - dari apa yang saya baca, dukungan Postgres JSON lebih baik, misalnya sepertinya mendukung indeks pada data dalam properti array JSON.
Giedrius
1
"... bidang dalam bidang JSONB tidak dapat dimodifikasi secara individual dengan kueri UPDATE; Anda harus mengganti seluruh konten bidang JSONB." Ini sudah ketinggalan zaman, bukan? Ada jsonb_set()fungsi di Postgres 9.5 dan yang lebih baru untuk hal ini. (Artikel yang Anda tautkan ke tautan pada gilirannya ke artikel yang lebih baru membahas penambahan fitur 9.5 .)
Wildcard
7

Biasanya orang melihat ke arah lain jika Anda menggunakannya untuk tabel pencarian, atau situasi lain di mana manfaatnya adalah tetap harus membuat tabel untuk satu atau dua nilai yang disimpan. Situasi yang Anda gambarkan, di mana Anda pada dasarnya menyimpan properti item, terdengar sangat normal (dan dinormalisasi). Memperluas tabel untuk menyimpan sejumlah variabel atribut item adalah ide yang buruk.

Untuk kasus umum menyimpan data yang berbeda dalam tabel tipis yang panjang ... Anda tidak perlu takut untuk membuat tabel baru jika Anda perlu, dan memiliki hanya satu atau dua tabel kurus panjang tidak jauh lebih baik daripada hanya memiliki satu atau dua meja pendek gemuk.

Yang sedang berkata, saya terkenal karena menggunakan tabel EAV untuk logging. Mereka memang memiliki beberapa utilitas yang baik.

Satanicpuppy
sumber
Silakan tentukan "meja kurus" dan "meja gemuk".
Tulains Córdova
@ TulainsCórdova: Meja "kurus" akan menjadi satu dengan beberapa baris, dan banyak kolom, sedangkan tabel gemuk adalah satu dengan banyak kolom dan beberapa baris. Contohnya akan membangun tabel pencarian di mana Anda memiliki properti untuk mengatakan, buku. Tabel gemuk akan memiliki satu catatan per buku, dengan banyak kolom untuk bagian data tertentu, sementara tabel tipis mungkin memiliki empat kolom id, buku, field_name, field_data. Keuntungan dari yang pertama adalah bahwa ada lebih sedikit catatan, tetapi negatif adalah bahwa beberapa bidang mungkin kosong, dan semuanya lebih sulit untuk diperluas.
Satanicpuppy
@ Setanpuppy Saya pikir definisi kurus / gemuk Anda tercampur - mereka adalah sama. Apakah maksud Anda bahwa tabel kurus memiliki beberapa kolom dan banyak baris?
Charles Wood
1

EAV mengubah masalah struktur eksplisit, menjadi persepsi tersirat. Daripada mengatakan X adalah tabel dengan kolom A dan B. Anda menyiratkan bahwa kolom A dan B membentuk tabel X. Itu kebalikannya dalam satu hal tetapi tidak ada pemetaan satu-ke-satu, tentu saja. Anda bisa mengatakan bahwa A dan B memetakan ke tabel (atau ketik) X dan Y. Ini bisa menjadi penting dalam domain yang lebih terlibat di mana konteks penting.

Saya telah mempelajari Datomic, untuk jenis pendekatan ini dan saya pikir ini adalah sistem yang sangat berguna dan kuat dengan batasan pada apa yang harus Anda lakukan dengannya (bukan berarti Anda tidak bisa).

Bahwa EAV akan lambat, atau "memberi Anda cukup tali untuk menggantung diri" bukanlah pernyataan yang akan saya setujui. Sebaliknya, saya akan lebih menekankan pada kekuatan EAV dan jika itu sesuai dengan ruang masalah Anda, Anda harus mempertimbangkannya.

Pengalaman saya adalah bahwa itu adalah pendekatan pemodelan yang indah dan hampir tak terbatas . Khususnya, dalam kasus Datomic, mereka mengenakan set semantik di atas segalanya. Setiap keputusan pemodelan yang memodelkan suatu hubungan dapat dengan bebas beralih dari satu, ke banyak tanpa harus mendesain ulang kolom / tabel. Anda juga dapat kembali selama kendala tidak melanggar invarian. Semuanya sama di bawah tenda.

Masalah dengan EAV ada di pikiran saya adalah kurangnya implementasi seperti Datomic. Karena ini adalah pertanyaan tentang EAV saya tidak ingin membicarakan tentang Datomic tetapi itu adalah salah satu hal di mana saya pikir mereka mendapatkan segalanya dengan benar sehubungan dengan EAV.

John Leidegren
sumber