EAV - มันแย่จริง ๆ ในทุกสถานการณ์หรือไม่?


65

ฉันคิดว่าจะใช้โมเดลเอนทิตีแอตทริบิวต์ - ค่า (EAV)สำหรับบางสิ่งในโครงการหนึ่ง แต่คำถามทั้งหมดเกี่ยวกับเรื่องนี้ใน Stack Overflowจบลงด้วยคำตอบที่เรียกว่า EAV เป็นรูปแบบการต่อต้าน

แต่ฉันสงสัยว่ามันผิดในทุกกรณีหรือไม่

สมมติว่าเอนทิตีผลิตภัณฑ์ของร้านค้ามันมีคุณสมบัติทั่วไปเช่นชื่อคำอธิบายรูปภาพและราคาที่มีส่วนร่วมในหลาย ๆ ตรรกะและมีคุณสมบัติที่เป็นเอกลักษณ์ (กึ่ง) เช่นนาฬิกาและลูกบอลชายหาดจะอธิบายด้วยแง่มุมที่แตกต่างกันโดยสิ้นเชิง ดังนั้นฉันคิดว่า EAV จะเหมาะสำหรับการจัดเก็บคุณลักษณะเฉพาะ (กึ่ง) เหล่านั้น

ทั้งหมดนี้เป็นการสมมติว่าสำหรับการแสดงรายการผลิตภัณฑ์มันเป็นข้อมูลที่เพียงพอในตารางผลิตภัณฑ์ (นั่นหมายความว่าไม่มี EAV ที่เกี่ยวข้อง) และเมื่อแสดงผลิตภัณฑ์เดียว / เปรียบเทียบผลิตภัณฑ์สูงสุด 5 รายการ / ฯลฯ ใช้ข้อมูลที่บันทึกโดยใช้ EAV

ฉันเคยเห็นวิธีการดังกล่าวในการค้าของวีโอไอพีและค่อนข้างเป็นที่นิยมดังนั้นมีกรณีที่ EAV เหมาะสมหรือไม่


2
@busy_wait "Entity-Attibute-Value" โต๊ะ - ดูรุ่น Entity แอตทริบิวต์ที่มีมูลค่าในวิกิพีเดีย
Ross Patterson

สำหรับตัวอย่างของรูปแบบ EAV ที่ทำงานได้ดีจริงๆให้ดูที่ฐานข้อมูล Datomic มันเก็บทุกอย่างในรูปแบบ EAVT (T คือ "เวลา" จริง ๆ แล้วเหมือน id ธุรกรรม) [เอกสารการทำดัชนี] (docs.datomic.com/indexes.html) เอกสารของพวกเขาดูเหมือนจะแสดงได้ดีที่สุด สำหรับตัวอย่างของ EAV ทำงานออกชะมัดดูWordpress
Dan Ross

คำตอบ:


80

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
รักประโยคสุดท้าย
Zohar Peled

2
ลิงค์เน่า มีรุ่นแคชอยู่ที่ไหนสักแห่ง?
Wildcard

1
อย่าไปตามลิงค์ หน้าโหลดช้าและไม่เป็นประโยชน์ นอกจากนี้ฟอรัมแบบเก่าเช่นนั้นยังเหม็น ใช้ stack overflow แทน! โหวตคำตอบที่ดี / เป็นประโยชน์และกดถังขยะ
Jess

29

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


16
ฉันจะแทนที่ "บ่อยครั้ง" โดย "ต้องการความเป็นไปได้ที่จะเปลี่ยนในเวลาทำงาน"
Doc Brown

3
เราสามารถย่อให้สั้นลงอีกว่า Doc Brown โดยใช้คำว่า "ไดนามิก" ที่เข้าใจกันดี - EAV นั้นมีประโยชน์เมื่อรายการแอตทริบิวต์ของคุณอาจเปลี่ยนแปลงแบบไดนามิก
Alexander Mills

แม้ต่อไป "เมื่อแอตทริบิวต์ของคุณอาจมีการเปลี่ยนแปลง" - "ไดนามิก" เป็นบิตซ้ำซ้อนในบริบทนี้ :)
Wranorn

1
มันจำเป็นต้องมีประโยชน์มากกว่าการพูดว่าการมีรูปแบบสำหรับการเปลี่ยนคุณสมบัติดำเนินการCREATE TABLEสำหรับแอตทริบิวต์ใหม่หรือไม่?
Damian Yerrick

@DamianYerrick วิธีการที่น่าสนใจ คุณเคยใช้สิ่งนี้ในการผลิตหรือไม่?
digout

21

สมมติว่าเอนทิตีผลิตภัณฑ์ของร้านค้ามีคุณสมบัติทั่วไปเช่นชื่อคำอธิบายรูปภาพราคาและอื่น ๆ ที่มีส่วนร่วมในหลาย ๆ ตรรกะและมีคุณสมบัติที่เป็นเอกลักษณ์ (กึ่ง) เช่นนาฬิกาและลูกบอลชายหาดจะอธิบายโดยแง่มุมที่แตกต่างกันโดยสิ้นเชิง . ดังนั้นฉันคิดว่า EAV จะเหมาะสำหรับการจัดเก็บคุณลักษณะเฉพาะ (กึ่ง) เหล่านั้นหรือไม่

การใช้โครงสร้าง EAV สำหรับมีความหมายหลายอย่างที่ไม่ชอบ

คุณกำลังค้าขาย 'พื้นที่น้อยกว่าสำหรับแถวเพราะคุณไม่มี 100 คอลัมน์ที่null' ต่อต้าน 'คำค้นหาและแบบจำลองที่ซับซ้อนยิ่งขึ้น'

โดยทั่วไปแล้วการมี 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 และความซับซ้อนของการพยายามตรวจสอบหรือสอบถามกับโครงสร้างเหล่านั้น

ทางเลือกหรือการดัดแปลงรูปแบบที่ต้องพิจารณาแทนการใช้รหัสรูปแบบอิสระเพื่อให้มีอีกตารางหนึ่งที่มีคีย์ที่ถูกต้อง มันหมายถึงแทนที่จะทำการเปรียบเทียบสตริงในฐานข้อมูลคุณกำลังตรวจสอบความเท่าเทียมกันของรหัสต่างประเทศ การเปลี่ยนรหัสเองทำได้ในจุดเดียว คุณมีชุดของคีย์ที่รู้จักซึ่งหมายความว่าพวกเขาสามารถทำได้เป็น enum

คุณอาจมีตารางที่เกี่ยวข้องซึ่งมีคุณลักษณะของคลาสผลิตภัณฑ์เฉพาะ แผนกขายของชำอาจมีตารางอื่นที่มีคุณลักษณะหลายอย่างที่เกี่ยวข้องกับวัสดุก่อสร้างที่ไม่ต้องการ (และในทางกลับกัน)

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

มีบางครั้งที่ต้องเรียกใช้ตาราง EAV โดยเฉพาะ

พิจารณาสถานการณ์ที่คุณไม่เพียงแค่เขียนระบบสินค้าคงคลังสำหรับ บริษัท ของคุณที่คุณรู้จักทุกผลิตภัณฑ์และทุกคุณสมบัติ ตอนนี้คุณกำลังเขียนระบบสินค้าคงคลังเพื่อขายให้กับ บริษัท อื่น คุณไม่สามารถรู้ได้ทุกคุณสมบัติของทุกผลิตภัณฑ์ - พวกเขาจะต้องกำหนด

หนึ่งความคิดที่ออกมาคือ "เราจะแจ้งให้ลูกค้าปรับเปลี่ยนตาราง" และนี่คือที่ไม่ดีเพียง (คุณได้รับใน meta-การเขียนโปรแกรมสำหรับโครงสร้างตารางเพราะคุณไม่ได้รู้ว่าสิ่งที่เป็นที่ที่พวกเขาสามารถพระราชทานเลอะโครงสร้างหรือเสียหาย แอปพลิเคชันพวกเขามีสิทธิ์เข้าถึงในการทำสิ่งที่ผิดและความหมายของการเข้าถึงนั้นมีความสำคัญมาก) มีข้อมูลเพิ่มเติมเกี่ยวกับเส้นทางนี้ที่MVC4: วิธีสร้างแบบจำลองในเวลาทำงาน

คุณสร้างอินเทอร์เฟซการดูแลระบบแทนตาราง EAV แทนและอนุญาตให้ใช้ หากลูกค้าต้องการสร้างรายการสำหรับ 'polkadots' มันจะเข้าไปในตาราง EAV และคุณรู้วิธีจัดการกับมันแล้ว

ตัวอย่างนี้สามารถเห็นได้ในโมเดลฐานข้อมูลสำหรับ Redmineคุณสามารถดูตาราง custom_fields และตาราง custom_values ​​ซึ่งเป็นส่วนหนึ่งของ EAV ที่อนุญาตให้ขยายระบบ


โปรดทราบว่าหากคุณพบว่าโครงสร้างตารางทั้งหมดของคุณดูเหมือน EAV แทนที่จะเป็นเชิงสัมพันธ์คุณอาจต้องการดูรสชาติ KV ของ NoSQL (คาสซานดรา, เรดิส, Mongo ,. ... ) ตระหนักว่าสิ่งเหล่านี้มักจะมาพร้อมกับการแลกเปลี่ยนอื่น ๆในการออกแบบของพวกเขาที่อาจหรืออาจไม่เหมาะสมกับสิ่งที่คุณใช้ อย่างไรก็ตามได้รับการออกแบบมาโดยเฉพาะด้วยความตั้งใจของโครงสร้าง EAV

คุณอาจต้องการอ่านSQL vs NoSQL สำหรับระบบการจัดการสินค้าคงคลัง

ทำตามวิธีนี้กับฐานข้อมูล NoSQL เอกสาร (โซฟา, mongo), คุณสามารถพิจารณาแต่ละรายการสินค้าคงคลังเป็นเอกสารบนดิสก์ ... ดึงทุกอย่างในเอกสารเดียวได้อย่างรวดเร็ว นอกจากนี้เอกสารยังมีโครงสร้างเพื่อให้คุณสามารถดึงสิ่งใดสิ่งหนึ่งที่รวดเร็ว ในทางกลับกันการค้นหาเอกสารทั้งหมดเพื่อหาสิ่งที่ตรงกับแอตทริบิวต์เฉพาะสามารถมีประสิทธิภาพน้อยลง (เปรียบเทียบการใช้ 'grep' กับไฟล์ทั้งหมด) ... ทั้งหมดเป็นการแลกเปลี่ยน

อีกวิธีหนึ่งคือ LDAP โดยที่หนึ่งจะมีฐานที่มีรายการที่เกี่ยวข้องทั้งหมด แต่จะมีคลาสวัตถุเพิ่มเติมที่ใช้กับรายการประเภทอื่น ๆ (ดูระบบสินค้าคงคลังโดยใช้ LDAP )

เมื่อคุณไปตามเส้นทางนี้คุณอาจพบสิ่งที่ตรงกับสิ่งที่คุณกำลังมองหา ... แม้ว่าทุกอย่างจะมาพร้อมกับการแลกเปลี่ยนบางอย่าง


10

6 ปีต่อมา

ตอนนี้JSON ใน Postgres มาถึงแล้วเรามีอีกทางเลือกหนึ่งสำหรับผู้ที่ใช้ 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"}

นี่คือการแนะนำที่นุ่มนวลเพื่อ JSON ใน Postgres: https://www.compose.com/articles/is-postgresql-your-next-json-database/

โปรดทราบว่า Postgres จะจัดเก็บ JSONB ไม่ใช่ข้อความธรรมดา JSON และรองรับดัชนีในฟิลด์ภายในของเอกสาร / ฟิลด์ JSONB ในกรณีที่คุณค้นพบว่าคุณต้องการสอบถามข้อมูลจริง ๆ

นอกจากนี้โปรดทราบว่าเขตข้อมูลภายในเขตข้อมูล JSONB ไม่สามารถปรับเปลี่ยนทีละรายการด้วยแบบสอบถาม UPDATE คุณจะต้องแทนที่เนื้อหาทั้งหมดของฟิลด์ JSONB

คำตอบนี้อาจไม่ได้ตอบคำถามโดยตรง แต่มันเสนอทางเลือกให้กับรูปแบบ EAV ซึ่งทุกคนที่ไตร่ตรองคำถามต้นฉบับควรได้รับการพิจารณา


3
ฉันคิดว่ามันเป็นความคิดที่ดีในการโพสต์ทางเลือกอื่น เพียงเพื่อให้ผู้อื่นติดตาม MS SQL สนับสนุนคอลัมน์ XML ที่มีความสามารถในการจัดทำดัชนีพวกเขาชั่วขณะหนึ่งและเริ่มจากปี 2559 ที่สามารถทำเช่นเดียวกันกับ JSON (แม้ว่า JSON จะไม่ใช่ประเภทคอลัมน์ดั้งเดิมใน MS SQL คุณยังสามารถจัดทำดัชนีได้ ) ในทางกลับกัน - จากสิ่งที่ฉันอ่านการสนับสนุน Postgres JSON นั้นดีกว่าตัวอย่างเช่นดูเหมือนว่าจะสนับสนุนการทำดัชนีข้อมูลในคุณสมบัติอาร์เรย์ของ JSON
Giedrius

1
"... เขตข้อมูลภายในเขตข้อมูล JSONB ไม่สามารถแก้ไขแยกต่างหากด้วยแบบสอบถาม UPDATE คุณจะต้องแทนที่เนื้อหาทั้งหมดของเขตข้อมูล JSONB" นี่มันล้าสมัยแล้วใช่ไหม มีjsonb_set()ฟังก์ชั่นใน Postgres 9.5 และใหม่กว่าซึ่งมีไว้สำหรับสิ่งนี้ (บทความที่คุณเชื่อมโยงไปยังลิงก์หันไปหาบทความใหม่ที่พูดถึงการเพิ่มฟีเจอร์ 9.5 )
Wildcard

7

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

สำหรับกรณีทั่วไปของการจัดเก็บข้อมูลที่แตกต่างกันในตารางที่บางยาว ... คุณไม่ควรกลัวที่จะสร้างตารางใหม่หากคุณต้องการและการมีตารางผอมยาวเพียงหนึ่งหรือสองตารางนั้นไม่ดีไปกว่าการมีเพียงหนึ่งหรือสองตัวเท่านั้น ตารางไขมันสั้นสองตาราง

ที่ถูกกล่าวว่าฉันฉาวโฉ่สำหรับการใช้ตาราง EAV สำหรับการเข้าสู่ระบบ พวกเขามียูทิลิตี้ที่ดี


โปรดกำหนด "โต๊ะผอม" และ "โต๊ะอ้วน"
Tulains Córdova

@ TulainsCórdova: ตาราง "ผอม" จะเป็นหนึ่งเดียวกับแถวไม่กี่แถวและหลายคอลัมน์ในขณะที่ตารางอ้วนเป็นตารางที่มีคอลัมน์จำนวนมากและแถวไม่กี่แถว ตัวอย่างจะสร้างตารางการค้นหาที่คุณมีคุณสมบัติสำหรับการพูดหนังสือ ตารางไขมันจะมีหนึ่งระเบียนต่อเล่มโดยมีคอลัมน์จำนวนมากสำหรับข้อมูลบางส่วนในขณะที่ตารางบาง ๆ อาจมีสี่คอลัมน์ id, หนังสือ, field_name, field_data ข้อดีของข้อแรกคือมีระเบียนน้อยลง แต่ข้อเสียคือบางฟิลด์อาจว่างเปล่าและสิ่งทั้งหมดนั้นยากที่จะขยาย
Satanicpuppy

@Satanicpuppy ฉันคิดว่าคำนิยามผอม / อ้วนของคุณผสมกัน - มันเหมือนกัน คุณหมายถึงตารางผอมมีคอลัมน์น้อยและหลายแถวหรือไม่
Charles Wood

1

EAV เปลี่ยนปัญหาโครงสร้างที่ชัดเจนเพื่อการรับรู้โดยนัย แทนที่จะบอกว่า X คือตารางที่มีคอลัมน์ A และ B คุณหมายความว่าคอลัมน์ A และ B เป็นตาราง X ซึ่งเป็นสิ่งที่ตรงกันข้ามในแง่หนึ่ง แต่ไม่มีการทำแผนที่แบบหนึ่งต่อหนึ่งจำเป็นต้องมี คุณสามารถพูดได้ว่า A และ B ทั้งคู่แมปไปยังตาราง (หรือประเภท) X และ Y นี่อาจเป็นสิ่งสำคัญในโดเมนที่เกี่ยวข้องมากกว่าซึ่งบริบทมีความสำคัญ

ฉันได้ศึกษา Datomic สำหรับวิธีการแบบนี้และฉันคิดว่ามันเป็นระบบที่มีประโยชน์และทรงพลังโดยมีข้อ จำกัด เกี่ยวกับสิ่งที่คุณควรทำกับมัน (ไม่ใช่สิ่งที่คุณทำไม่ได้)

EAV นั้นจะช้าหรือ "ให้เชือกพอที่จะแขวนตัวเอง" ไม่ใช่คำแถลงที่ฉันเห็นด้วย แต่ฉันจะให้ความสำคัญกับจุดแข็งของ EAV และถ้ามันเหมาะสมกับพื้นที่ปัญหาของคุณคุณควรพิจารณา

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

ปัญหาเกี่ยวกับ EAV อยู่ในใจของฉันอยู่ที่การขาดการใช้งานเช่น Datomic เนื่องจากนี่เป็นคำถามเกี่ยวกับ EAV ฉันไม่ต้องการคลั่งเกี่ยวกับ Datomic แต่เป็นหนึ่งในสิ่งที่ฉันคิดว่าพวกเขาได้ทุกอย่างถูกต้องเกี่ยวกับ EAV

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.