Apakah ada nama untuk skema basis data nilai kunci ini?

68

Kami memproses umpan data rutin dari klien yang baru saja melakukan refactored database mereka dari formulir yang tampak familier (satu baris per entitas, satu kolom per atribut) ke yang tampaknya asing bagi saya (satu baris per entitas per atribut):

Sebelum: satu kolom per atribut

ID   Ht_cm   wt_kg   Age_yr  ... 
1      190      82     43    ...
2      170      60     22    ...
3      205      90     51    ...

Setelah: satu kolom untuk semua atribut

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

Apakah ada nama untuk struktur basis data ini? Apa keunggulan relatifnya? Cara lama tampaknya lebih mudah untuk menempatkan batasan validitas pada atribut tertentu (non-null, non-negatif, dll) dan lebih mudah untuk menghitung rata-rata. Tapi saya bisa melihat bagaimana mungkin lebih mudah untuk menambahkan atribut baru tanpa refactoring database. Apakah ini cara standar / disukai untuk penataan data?

prototipe
sumber

Jawaban:

91

Ini disebut Entity-Attribute-Value (juga kadang-kadang 'pasangan nama-nilai') dan ini merupakan kasus klasik "pasak bundar dalam lubang persegi" ketika orang menggunakan pola EAV dalam database relasional.

Berikut adalah daftar mengapa Anda tidak boleh menggunakan EAV:

  • Anda tidak dapat menggunakan tipe data. Tidak masalah jika nilainya adalah tanggal, angka atau uang (desimal). Itu akan selalu dipaksa untuk varchar. Ini bisa berupa apa saja dari masalah kinerja kecil hingga sakit usus besar (pernah harus mengejar variasi satu sen dalam laporan bulanan roll-up?).
  • Anda tidak dapat (dengan mudah) menegakkan batasan. Dibutuhkan jumlah kode yang konyol untuk menegakkan "Setiap orang harus memiliki ketinggian antara 0 dan 3 meter" atau "Usia tidak boleh nol dan> = 0", berlawanan dengan 1-2 baris yang masing-masing dari batasan tersebut akan menjadi dalam sistem yang dimodelkan dengan benar.
  • Terkait dengan di atas, Anda tidak dapat dengan mudah menjamin bahwa Anda mendapatkan informasi yang Anda butuhkan untuk setiap klien (usia mungkin hilang dari satu, maka yang berikutnya mungkin akan kehilangan tinggi badan mereka, dll.). Anda dapat melakukannya, tetapi jauh lebih sulit daripada itu SELECT height, weight, age FROM Client where height is null or weight is null.
  • Terkait lagi, data duplikat jauh lebih sulit untuk dideteksi (apa yang terjadi jika mereka memberi Anda dua usia untuk satu klien? De-EAVing data, seperti di bawah ini, akan memberi Anda dua baris hasil jika Anda memiliki satu atribut yang berlipat ganda. Jika satu klien memiliki dua entri terpisah untuk dua atribut, Anda akan mendapatkan empat baris dari kueri di bawah).
  • Anda bahkan tidak dapat menjamin bahwa nama atributnya konsisten. "Age_yr" mungkin menjadi "AGE_IN_YEARS" atau "age". (Harus diakui ini bukan masalah saat Anda menerima ekstrak versus ketika orang memasukkan data, tetapi tetap saja.)
  • Segala jenis permintaan nontrivial adalah bencana total. Untuk menghubungkan kembali sistem EAV tiga atribut sehingga Anda dapat menanyakannya secara rasional membutuhkan tiga gabungan dari tabel EAV.

Membandingkan:

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"

Untuk:

SELECT c.ID, c.Ht_cm, c.Wt_kg, c.Age_yr
FROM Client c

Berikut adalah daftar (sangat singkat) kapan Anda harus menggunakan EAV:

  • Ketika sama sekali tidak ada jalan keluar dan Anda harus mendukung data tanpa skema di database Anda.
  • Ketika Anda hanya perlu menyimpan "barang" dan jangan berharap harus membutuhkannya dalam bentuk yang lebih terstruktur. Namun waspadalah, monster itu bernama "mengubah persyaratan".

Aku tahu aku hanya menghabiskan seluruh posting ini merinci mengapa EAV adalah ide yang buruk dalam banyak kasus - tetapi ada yang beberapa kasus di mana diperlukan / tidak dapat dihindari. Namun, sebagian besar waktu (termasuk contoh di atas), itu akan jauh lebih merepotkan daripada nilainya. Jika Anda memiliki persyaratan untuk dukungan luas input data tipe EAV, Anda harus melihat menyimpannya dalam sistem nilai kunci, misalnya Hadoop / HBase, CouchDB, MongoDB, Cassandra, BerkeleyDB.

Simon Righarts
sumber
7
+1 dengan pemberitahuan kecil: Anda bisa menggunakan tipe data jika Anda meletakkan nilai dari tipe yang berbeda di tabel yang berbeda (yah, bukan EAV klasik, tapi semacam peningkatan). (Tapi kemudian muncul pertanyaan tambahan: bagaimana Anda tahu tipe atribut baru?)
dezso
4
Setuju, tapi saya akan menambahkan bahwa EAV juga merupakan pendekatan yang baik untuk digunakan ketika Anda menyimpan daftar hal-hal yang secara semantis tidak relevan dengan sistem Anda (bukan hanya tanpa skema). Misalnya katalog produk online di mana fitur produk perlu disimpan dan terdaftar. Anda memiliki daftar pasangan kunci / nilai untuk dimuntahkan, tetapi sistem tidak benar-benar tahu atau peduli tentang apa kunci atau nilai itu. Dalam situasi itu, bahaya EAV tidak relevan.
Joel Brown
10
@ JoelBrown Anda tidak peduli SEKARANG, tetapi jika seorang VP bertanya untuk mengetahui berapa banyak kemeja dalam katalog yang memiliki kancing coklat dan kancing kerah, itu akan menyebalkan dari permintaan untuk menulis. EAV itu sendiri biasanya menunjukkan kurangnya perencanaan atau pandangan ke depan.
JNK
2
@ JoelBrown Saya tidak setuju bahwa ia memiliki penggunaan (sangat kecil sangat sempit). Tetapi jika info tersebut kemungkinan akan ditanyakan dengan cara terstruktur apa pun, itu mungkin seharusnya tidak ada di EAV
JNK
4
@ JoelBrown Jika persyaratan bisnis Anda atau data yang Anda simpan berubah, demikian juga model data Anda . Model data Anda tidak harus diukir di atas batu. Juga, untuk basis data relasional, 99% dari waktu orang menggunakan EAV alasan mereka bermuara pada "Saya tidak ingin menghabiskan waktu memikirkan tentang cara menyimpan data saya" daripada "Mempertimbangkan semua pola dan model basis data yang saya tahu, EAV berfungsi paling baik untuk kumpulan data ini ". Untuk mengulang - ada yang kasus di mana EAV berguna (dan bahkan mungkin jawaban yang 'benar'), tapi mereka sedikit dan jauh antara.
Simon Righarts
18

Nilai Atribut Entitas (EAV)

Ini dianggap sebagai anti-pola oleh banyak orang, termasuk saya.

Berikut adalah alternatif Anda:

  1. menggunakan pewarisan tabel database

  2. menggunakan data XML dan fungsi SQLXML

  3. gunakan database nosql, seperti HBase

Neil McGuigan
sumber
3
Jelas merupakan anti-pola untuk sebagian besar kasus penggunaan. Jika Anda memiliki kumpulan data yang sangat kecil dan kinerja tidak masalah itu mungkin bekerja untuk Anda.
JNK
16

Dalam PostgreSQL, satu cara yang sangat baik untuk menangani struktur EAV adalah modul tambahan hstore, tersedia untuk versi 8.4 atau lebih baru. Saya mengutip manual:

Modul ini mengimplementasikan hstoretipe data untuk menyimpan set pasangan kunci / nilai dalam nilai PostgreSQL tunggal. Ini dapat berguna dalam berbagai skenario, seperti baris dengan banyak atribut yang jarang diperiksa, atau data semi-terstruktur. Kunci dan nilai hanyalah string teks.

Sejak Postgres 9.2 ada juga jsontipe dan sejumlah fungsi untuk digunakan ( sebagian besar ditambahkan dengan 9.3 ).

Postgres 9.4 menambahkan tipe data "binary JSON" (sebagian besar unggul!) Ke jsonbdaftar opsi. Dengan opsi indeks lanjutan.

Erwin Brandstetter
sumber
10

Jika Anda memiliki database yang menggunakan struktur EAV, dimungkinkan untuk menanyakan data dengan berbagai cara.

@ Simon menjawab sudah menunjukkan bagaimana melakukan kueri menggunakan beberapa gabungan.

Sampel Data yang Digunakan:

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);

Jika Anda menggunakan RDBMS yang memiliki PIVOTfungsi ( SQL Server 2005+ / Oracle 11g + ) maka Anda dapat meminta data dengan cara berikut:

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;

Lihat SQL Fiddle dengan Demo

Jika Anda tidak memiliki akses ke suatu PIVOTfungsi, maka Anda dapat menggunakan fungsi agregat dengan CASEpernyataan untuk mengembalikan data:

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

Lihat SQL Fiddle dengan Demo

Kedua kueri ini akan mengembalikan data dalam hasil:

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

Lucu melihat bagaimana model EAV db dikritik dan bahkan dianggap sebagai "anti-pola" oleh beberapa orang.

Sejauh yang saya ketahui, kerugian utama adalah:

  • Kurva pembelajaran lebih curam jika Anda mendapatkan proyek yang sudah mulai menggunakan EAV beberapa waktu lalu. Memang, pertanyaannya sulit karena Anda sangat meningkatkan jumlah gabungan (dan tabel) sehingga akan meminta lebih banyak waktu untuk Anda mengerti. Lihat saja proyek Magento dan lihat bagaimana pengembang eksternal untuk proyek mengalami kesulitan mengerjakan DB, namun dokumentasinya dipertahankan dengan baik.
  • Tidak cocok untuk pelaporan , jika Anda perlu mendapatkan jumlah orang yang namanya dimulai dengan "M" dll ...

Namun, Anda seharusnya tidak membuang solusi ini, dan inilah alasannya:

  • Simon berbicara tentang monster yang disebut "mengubah persyaratan". Saya suka ungkapan ini :). Dan IMHO, inilah tepatnya mengapa EAV bisa menjadi kandidat yang baik, karena ini sangat cocok untuk "perubahan" , karena Anda dapat menambahkan atribut sebanyak yang Anda inginkan dengan mudah. Tentu saja itu tergantung pada persyaratan yang kami ubah. Jika kita berbicara tentang bisnis yang sama sekali baru, tentu saja Anda harus meninjau dataModel Anda, tetapi EAV menawarkan banyak fleksibilitas. Hanya karena meminta lebih keras, bukan berarti ini kurang menarik.
  • Juga dikatakan bahwa "Anda tidak dapat menggunakan tipe data." : Ini salah . Anda mungkin memiliki beberapa tabel nilai , satu untuk setiap tipe data. Anda kemudian harus menentukan di tabel atribut Anda yang mana tipe data adalah atribut Anda. Bahkan, campuran dari hubungan klasik / EAV dengan hubungan kelas menawarkan banyak potensi menarik dalam desain dataBase.
Melvin PRESSOUYRE
sumber
2
Kurva pembelajaran lebih curam untuk desain EAV pertama yang dijumpai. Setelah itu, semua terlihat sama.
ypercubeᵀᴹ
1
Komentar temp: Saya tidak mengerti mengapa klaim "tidak cocok untuk melaporkan". EAV tampak hebat untuk pelaporan. Pilih ObjectId dari eav.values ​​di mana propertyId = nama dan nilai seperti 'm%'. Perubahan pada skema virtual (misalnya menambahkan properti) dapat dimasukkan dalam antarmuka pelaporan dinamis (seperti dropdown) tanpa harus melakukan kompilasi ulang.
crokusek