มีชื่อสำหรับสกีมาฐานข้อมูลนี้ของค่าคีย์หรือไม่?


68

เราประมวลผลฟีดข้อมูลประจำจากลูกค้าที่เพิ่งปรับโครงสร้างฐานข้อมูลของพวกเขาจากรูปแบบที่ดูเหมือนคุ้นเคย (หนึ่งแถวต่อเอนทิตี้หนึ่งคอลัมน์ต่อแอตทริบิวต์) ไปยังอันที่ดูเหมือนฉันไม่คุ้นเคย (หนึ่งแถวต่อเอนทิตีต่อแอตทริบิวต์):

ก่อนหน้า: หนึ่งคอลัมน์ต่อแอตทริบิวต์

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

หลัง: หนึ่งคอลัมน์สำหรับแอตทริบิวต์ทั้งหมด

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

มีชื่อสำหรับโครงสร้างฐานข้อมูลนี้หรือไม่? ข้อดีที่สัมพันธ์กันคืออะไร วิธีการแบบเก่าดูเหมือนจะง่ายกว่าในการวางข้อ จำกัด ความถูกต้องบนแอตทริบิวต์ที่เฉพาะเจาะจง (ไม่เป็นโมฆะไม่เป็นลบ ฯลฯ ) และคำนวณค่าเฉลี่ยได้ง่ายขึ้น แต่ฉันสามารถดูว่ามันจะง่ายขึ้นในการเพิ่มคุณสมบัติใหม่โดยไม่ต้องปรับฐานข้อมูล นี่เป็นวิธีมาตรฐานในการจัดโครงสร้างข้อมูลหรือไม่?

คำตอบ:


91

มันเรียกว่า Entity-Attribute-Value (บางครั้งเรียกว่า 'คู่ชื่อ - ค่า') และเป็นกรณีคลาสสิกของ "หมุดกลมในช่องสี่เหลี่ยม" เมื่อผู้คนใช้รูปแบบ EAV ในฐานข้อมูลเชิงสัมพันธ์

นี่คือรายการสาเหตุที่คุณไม่ควรใช้ EAV:

  • คุณไม่สามารถใช้ประเภทข้อมูล ไม่สำคัญว่าค่าจะเป็นวันที่ตัวเลขหรือเงิน (ทศนิยม) มันจะถูกข่มขู่ให้ varchar เสมอ สิ่งนี้อาจเป็นอะไรก็ได้ตั้งแต่ปัญหาประสิทธิภาพเล็กน้อยไปจนถึงอาการปวดท้องอย่างมาก (เคยต้องไล่ตามความผันแปรหนึ่งเปอร์เซ็นต์ในรายงานสรุปรายเดือนหรือไม่)
  • คุณไม่สามารถบังคับใช้ข้อ จำกัด ได้อย่างง่ายดาย มันต้องใช้รหัสที่ไร้สาระในการบังคับใช้ "ทุกคนต้องมีความสูงระหว่าง 0 ถึง 3 เมตร" หรือ "อายุจะต้องไม่เป็นโมฆะและ> = 0" ซึ่งตรงข้ามกับ 1-2 บรรทัดที่แต่ละข้อ จำกัด เหล่านั้นจะเป็น ในระบบที่มีรูปแบบที่เหมาะสม
  • ที่เกี่ยวข้องกับด้านบนคุณไม่สามารถรับประกันได้อย่างง่ายดายว่าคุณได้รับข้อมูลที่คุณต้องการสำหรับลูกค้าแต่ละราย (อายุอาจหายไปจากลูกค้าคนหนึ่งจากนั้นลูกค้าคนถัดไปอาจหายไปจากความสูงเป็นต้น) คุณสามารถทำมัน SELECT height, weight, age FROM Client where height is null or weight is nullแต่มันเป็นนรกของมากยากกว่า
  • เกี่ยวข้องกันอีกครั้งข้อมูลที่ซ้ำกันจะตรวจจับได้ยากกว่า (จะเกิดอะไรขึ้นถ้าพวกเขาให้คุณอายุสองขวบสำหรับลูกค้าหนึ่งคนการลบข้อมูลดังกล่าวด้านล่างจะให้ผลลัพธ์สองแถวแก่คุณหากคุณมีแอตทริบิวต์สองเท่า มีสองรายการแยกกันสำหรับสองแอตทริบิวต์คุณจะได้รับสี่แถวจากแบบสอบถามด้านล่าง)
  • คุณไม่สามารถรับประกันได้ว่าชื่อแอตทริบิวต์จะสอดคล้องกัน "Age_yr" อาจกลายเป็น "AGE_IN_YEARS" หรือ "อายุ" (เป็นที่ยอมรับว่าเป็นปัญหาน้อยเมื่อคุณได้รับสารสกัดเมื่อเทียบกับเมื่อผู้คนใส่ข้อมูล แต่ยังคงอยู่)
  • การเรียงลำดับข้อความค้นหาที่ไม่เกี่ยวกับเหตุการณ์เป็นความหายนะที่สมบูรณ์ ในการสร้างความสัมพันธ์ระบบ EAV แบบสามแอตทริบิวต์เพื่อให้คุณสามารถสืบค้นแบบมีเหตุผลต้องใช้การรวมสามตารางของ EAV

เปรียบเทียบ:

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 ยังเป็นวิธีการที่ดีที่จะใช้เมื่อคุณเก็บรายการสิ่งต่าง ๆ ที่ไม่เกี่ยวข้องกับระบบของคุณ (ไม่เพียงแค่ schema-less) ตัวอย่างเช่นแคตตาล็อกผลิตภัณฑ์ออนไลน์ที่จำเป็นต้องจัดเก็บและแสดงคุณสมบัติของผลิตภัณฑ์ คุณมีรายการคู่คีย์ / ค่าที่จะสำรอกกลับคืน แต่ระบบไม่ทราบหรือสนใจจริง ๆ ว่าคีย์หรือค่าเหล่านั้นเกี่ยวกับอะไร ในสถานการณ์เช่นนั้นภัยของ EAV ไม่เกี่ยวข้อง
Joel Brown

10
@JoelBrown คุณไม่สนใจตอนนี้ แต่ถ้ารองประธาน VP ขอให้รู้ว่าจำนวนเสื้อในแค็ตตาล็อกมีทั้งปุ่มสีน้ำตาลและปุ่มลงปกมันจะเป็นคำที่ใช้เขียน โดยปกติแล้ว EAV นั้นบ่งบอกถึงการขาดการวางแผนหรือการมองการณ์ไกล
JNK

2
@ JoelBrown ฉันไม่เห็นด้วยกับการใช้งาน (แคบมาก ๆ ) แต่ถ้าข้อมูลมีแนวโน้มที่จะถูกสอบถามในรูปแบบที่มีโครงสร้างใด ๆ มันอาจไม่ควรอยู่ใน EAV
JNK

4
@JoelBrown หากความต้องการทางธุรกิจของคุณหรือข้อมูลที่คุณกำลังจัดเก็บการเปลี่ยนแปลงดังนั้นควรรูปแบบข้อมูลของคุณ แบบจำลองข้อมูลของคุณไม่ควรถูกแกะสลักด้วยหิน นอกจากนี้สำหรับฐานข้อมูลเชิงสัมพันธ์ 99% ของเวลาที่ผู้คนใช้ EAV เหตุผลของพวกเขาทวีความรุนแรงลงถึง "ฉันไม่ต้องการใช้เวลาคิดเกี่ยวกับวิธีการจัดเก็บข้อมูลของฉัน" แทนที่จะ "พิจารณารูปแบบฐานข้อมูลและแบบจำลองทั้งหมดที่ฉันรู้ EAV ทำงานได้ดีที่สุดสำหรับชุดข้อมูลนี้ " ในการทำซ้ำ - มีหลายกรณีที่ EAV มีประโยชน์ (และอาจเป็นคำตอบที่ 'ถูกต้อง') แต่ก็มีอยู่น้อยมาก
Simon Righarts

18

ค่าแอตทริบิวต์ของเอนทิตี (EAV)

มันถือว่าเป็นรูปแบบการต่อต้านโดยหลายคนรวมถึงฉัน

นี่คือทางเลือกของคุณ:

  1. ใช้การสืบทอดตารางฐานข้อมูล

  2. ใช้ข้อมูล XML และฟังก์ชั่น SQLXML

  3. ใช้ฐานข้อมูล nosql เช่น HBase


3
รูปแบบการต่อต้านแน่นอนสำหรับกรณีการใช้งานส่วนใหญ่ หากคุณมีชุดข้อมูลและประสิทธิภาพการทำงานขนาดเล็กจริง ๆ ไม่สำคัญว่ามันจะทำงานให้คุณได้
JNK

16

ใน PostgreSQL วิธีที่ดีอย่างหนึ่งในการจัดการกับโครงสร้าง EAV คือโมดูลเพิ่มเติมhstoreซึ่งมีให้ในเวอร์ชัน 8.4 หรือใหม่กว่า ฉันพูดคู่มือ:

โมดูลนี้ใช้hstoreชนิดข้อมูลสำหรับการจัดเก็บชุดของคู่คีย์ / ค่าภายในค่า PostgreSQL เดียว สิ่งนี้มีประโยชน์ในสถานการณ์ต่าง ๆ เช่นแถวที่มีคุณลักษณะหลายอย่างที่ไม่ค่อยได้รับการตรวจสอบหรือข้อมูลกึ่งโครงสร้าง คีย์และค่าเป็นเพียงสตริงข้อความ

ตั้งแต่ Postgres 9.2 มีjsonประเภทและโฮสต์ของฟังก์ชั่นให้ใช้งานได้ ( ส่วนใหญ่เพิ่มด้วย 9.3 )

Postgres 9.4 เพิ่มประเภทข้อมูล "binary JSON" (ส่วนใหญ่เหนือกว่า!) jsonbลงในรายการตัวเลือก ด้วยตัวเลือกดัชนีขั้นสูง


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

หากคุณใช้ RDBMS ที่มีPIVOTฟังก์ชั่น ( SQL Server 2005+ / Oracle 11g + ) คุณสามารถสืบค้นข้อมูลด้วยวิธีต่อไปนี้:

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 พร้อมเดโม

หากคุณไม่มีสิทธิ์เข้าถึง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 พร้อมเดโม

ข้อความค้นหาทั้งสองนี้จะส่งคืนข้อมูลในผลลัพธ์:

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

10

ตลกที่เห็นว่า EAV db model ถูกวิพากษ์วิจารณ์และถือเป็น "anti-pattern" โดยบางคน

เท่าที่ฉันกังวลข้อเสียที่สำคัญคือ:

  • เส้นโค้งการเรียนรู้นั้นชันถ้าคุณทำโปรเจ็กต์ซึ่งเริ่มใช้ EAV ไปแล้วเมื่อไม่นานมานี้ แท้จริงแล้วการสืบค้นนั้นยากเมื่อคุณเพิ่มจำนวนการเข้าร่วม (และตาราง) อย่างมากและจะทำให้คุณมีเวลามากขึ้นในการทำความเข้าใจ เพียงแค่ดูที่โครงการวีโอไอพีและดูว่าการพัฒนาภายนอกของโครงการมีช่วงเวลาที่ยากลำบากในการทำงานกับฐานข้อมูลได้อย่างไร
  • ไม่เหมาะสำหรับการรายงานหากคุณต้องการรับจำนวนคนที่ชื่อขึ้นต้นด้วย "M" ฯลฯ ...

อย่างไรก็ตามคุณไม่ควรละทิ้งแนวทางนี้และนี่คือเหตุผล:

  • Simon พูดคุยเกี่ยวกับสัตว์ประหลาดที่เรียกว่า "การเปลี่ยนแปลงข้อกำหนด" ฉันชอบการแสดงออกนี้ :) และ IMHO นี่คือเหตุผลที่ EAV อาจเป็นตัวเลือกที่ดีเพราะมันเหมาะสำหรับ "การเปลี่ยนแปลง"เนื่องจากคุณสามารถเพิ่มคุณสมบัติได้มากเท่าที่คุณต้องการได้อย่างง่ายดาย แน่นอนมันขึ้นอยู่กับข้อกำหนดที่เรากำลังเปลี่ยนแปลง หากเรากำลังพูดถึงธุรกิจใหม่ทั้งหมดแน่นอนว่าคุณจะต้องตรวจสอบ dataModel ของคุณ แต่ EAV ให้ความยืดหยุ่นสูง เพียงเพราะมันขอความเข้มงวดมากขึ้นไม่ได้หมายความว่ามันน่าสนใจน้อยกว่า
  • มีการกล่าวด้วยว่า "คุณไม่สามารถใช้ประเภทข้อมูล" : นี้เป็นธรรม คุณอาจมีตารางค่าได้หลายตารางสำหรับแต่ละประเภทข้อมูล จากนั้นคุณต้องระบุในตารางแอตทริบิวต์ซึ่ง dataType เป็นแอตทริบิวต์ของคุณ ในความเป็นจริงการผสมผสานของความสัมพันธ์แบบคลาสสิกกับ EAV กับความสัมพันธ์ในชั้นเรียนนั้นมีศักยภาพที่น่าสนใจมากมายในการออกแบบฐานข้อมูล

2
เส้นโค้งการเรียนรู้นั้นชันสำหรับ EAV แรกที่พบหนึ่งการออกแบบ หลังจากนั้นทุกคนก็เหมือนกัน
ypercubeᵀᴹ

1
ความคิดเห็นชั่วคราว: ฉันไม่เข้าใจว่าทำไมการอ้างสิทธิ์ "ไม่เหมาะสำหรับการรายงาน" EAV ดูยอดเยี่ยมสำหรับการรายงาน เลือก ObjectId จาก eav.values ​​โดยที่ propertyId = ชื่อและค่าเช่น 'm%' การเปลี่ยนแปลงสคีมาเสมือน (เช่นการเพิ่มคุณสมบัติ) สามารถรวมอยู่ในอินเทอร์เฟซการรายงานแบบไดนามิกใด ๆ (เช่นแบบเลื่อนลง) โดยไม่ต้องคอมไพล์ใหม่
crokusek
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.