Có một tên cho lược đồ cơ sở dữ liệu này của các giá trị chính?


68

Chúng tôi xử lý nguồn cấp dữ liệu thông thường từ một khách hàng vừa tái cấu trúc cơ sở dữ liệu của họ từ một hình thức có vẻ quen thuộc (một hàng cho mỗi thực thể, một cột cho mỗi thuộc tính) thành một thứ dường như xa lạ với tôi (một hàng cho mỗi thực thể cho mỗi thuộc tính):

Trước: một cột cho mỗi thuộc tính

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

Sau: một cột cho tất cả các thuộc tính

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

Có một tên cho cấu trúc cơ sở dữ liệu này? Những lợi thế tương đối là gì? Cách cũ có vẻ dễ dàng hơn để đặt các ràng buộc hợp lệ trên các thuộc tính cụ thể (không null, không âm, v.v.) và dễ dàng hơn để tính trung bình. Nhưng tôi có thể thấy làm thế nào có thể dễ dàng hơn để thêm các thuộc tính mới mà không cần cấu trúc lại cơ sở dữ liệu. Đây có phải là một cách tiêu chuẩn / ưa thích của cấu trúc dữ liệu?

Câu trả lời:


91

Nó được gọi là Entity-Attribution-Value (đôi khi cũng là 'cặp giá trị tên') và đó là trường hợp kinh điển của "một cái chốt tròn trong một lỗ vuông" khi mọi người sử dụng mẫu EAV trong cơ sở dữ liệu quan hệ.

Đây là danh sách lý do tại sao bạn không nên sử dụng EAV:

  • Bạn không thể sử dụng các loại dữ liệu. Không có vấn đề gì nếu giá trị là ngày, số hoặc tiền (số thập phân). Nó sẽ luôn bị ép buộc vào varchar. Đây có thể là bất cứ điều gì từ một vấn đề hiệu suất nhỏ đến một cơn đau ruột lớn (có bao giờ phải theo đuổi một biến thể một xu trong báo cáo cuộn lên hàng tháng không?).
  • Bạn không thể (dễ dàng) thực thi các ràng buộc. Nó đòi hỏi một số lượng mã vô lý để thực thi "Mọi người cần phải có chiều cao trong khoảng từ 0 đến 3 mét" hoặc "Tuổi không được rỗng và> = 0", trái ngược với 1-2 dòng mà mỗi ràng buộc đó sẽ là trong một hệ thống được mô hình hóa đúng.
  • Liên quan đến ở trên, bạn không thể dễ dàng đảm bảo rằng bạn có được thông tin bạn cần cho mỗi khách hàng (tuổi có thể bị thiếu từ một, sau đó có thể thiếu chiều cao của họ, v.v.). Bạn có thể làm điều đó, nhưng đó là một địa ngục khó khăn hơn nhiều SELECT height, weight, age FROM Client where height is null or weight is null.
  • Liên quan lại, dữ liệu trùng lặp khó phát hiện hơn rất nhiều (điều gì xảy ra nếu chúng cho bạn hai tuổi cho một khách hàng? Khử dữ liệu, như bên dưới, sẽ cung cấp cho bạn hai hàng kết quả nếu bạn có một thuộc tính nhân đôi. có hai mục nhập riêng biệt cho hai thuộc tính, bạn sẽ nhận được bốn hàng từ truy vấn bên dưới).
  • Bạn thậm chí không thể đảm bảo rằng các tên thuộc tính là nhất quán. "Age_yr" có thể trở thành "AGE_IN_YEARS" hoặc "tuổi". (Phải thừa nhận rằng đây không phải là vấn đề khi bạn nhận được trích xuất so với khi mọi người đang chèn dữ liệu.)
  • Bất kỳ loại truy vấn không cần thiết là một thảm họa hoàn chỉnh. Để liên kết hệ thống EAV ba thuộc tính để bạn có thể truy vấn nó theo cách hợp lý, cần có ba phép nối của bảng EAV.

So sánh:

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"

Đến:

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

Đây là danh sách (rất ngắn) về thời điểm bạn nên sử dụng EAV:

  • Khi hoàn toàn không có cách nào khác và bạn phải hỗ trợ dữ liệu không có lược đồ trong cơ sở dữ liệu của bạn.
  • Khi bạn chỉ cần lưu trữ "công cụ" và không cần phải có nó ở dạng có cấu trúc chặt chẽ hơn. Hãy coi chừng, con quái vật gọi là "thay đổi yêu cầu".

Tôi biết tôi chỉ cần bỏ ra toàn bộ bài này quy định chi tiết tại sao EAV là một ý tưởng khủng khiếp trong nhiều trường hợp - nhưng có một vài trường hợp cần thiết / không thể tránh khỏi. tuy nhiên, hầu hết thời gian (bao gồm cả ví dụ ở trên), nó sẽ rắc rối hơn nhiều so với giá trị của nó. Nếu bạn có yêu cầu hỗ trợ rộng rãi cho đầu vào dữ liệu kiểu EAV, bạn nên xem việc lưu trữ chúng trong hệ thống khóa-giá trị, ví dụ: Hadoop / HBase, CouchDB, MongoDB, Cassandra, BerkeleyDB.


7
+1 với một thông báo nhỏ: bạn có thể sử dụng các loại dữ liệu nếu bạn đặt các giá trị của các loại khác nhau trong các bảng khác nhau (tốt, không phải EAV cổ điển, nhưng sắp xếp cải tiến). (Nhưng sau đó có thêm một câu hỏi: làm thế nào để bạn biết loại thuộc tính mới?)
dezso

4
Đồng ý, nhưng tôi sẽ nói thêm rằng EAV cũng là một cách tiếp cận tốt để sử dụng khi bạn đang giữ một danh sách những thứ không liên quan đến ngữ nghĩa đối với hệ thống của bạn (không chỉ là lược đồ). Ví dụ, một danh mục sản phẩm trực tuyến nơi các tính năng sản phẩm cần được lưu trữ và liệt kê. Bạn có một danh sách các cặp khóa / giá trị để hồi sinh, nhưng hệ thống không thực sự biết hoặc quan tâm đến những khóa hoặc giá trị đó nói về cái gì. Trong tình huống đó, sự nguy hiểm của EAV là không liên quan.
Joel Brown

10
@JoelBrown Bạn không quan tâm NGAY BÂY GIỜ, nhưng nếu xuống đường, VP yêu cầu biết có bao nhiêu áo trong danh mục có cả nút màu nâu và nút xuống cổ áo, đó sẽ là một câu hỏi khó để viết. Bản thân EAV thường chỉ ra sự thiếu kế hoạch hoặc tầm nhìn xa.
JNK

2
@JoelBrown Tôi không đồng ý rằng nó có cách sử dụng (rất nhỏ rất hẹp). Nhưng nếu thông tin có thể sẽ được truy vấn theo bất kỳ cấu trúc nào thì có lẽ không nên có trong EAV
JNK

4
@JoelBrown Nếu yêu cầu kinh doanh của bạn hoặc dữ liệu bạn đang lưu trữ thay đổi, thì mô hình dữ liệu của bạn cũng vậy . Mô hình dữ liệu của bạn không nên được khắc trên đá. Ngoài ra, đối với cơ sở dữ liệu quan hệ, 99% thời gian mọi người sử dụng EAV lý do của họ sôi nổi là "Tôi không muốn dành thời gian suy nghĩ về cách lưu trữ dữ liệu của mình" thay vì "Xem xét tất cả các mẫu và mô hình cơ sở dữ liệu mà tôi biết, EAV hoạt động tốt nhất cho tập dữ liệu này ". Nhắc lại - có những trường hợp EAV hữu ích (và thậm chí có thể là câu trả lời 'đúng'), nhưng chúng rất ít.
Simon Righarts

18

Giá trị thuộc tính thực thể (EAV)

Nó được coi là một mô hình chống đối với nhiều người, bao gồm cả tôi.

Dưới đây là các lựa chọn thay thế của bạn:

  1. sử dụng kế thừa bảng cơ sở dữ liệu

  2. sử dụng dữ liệu XML và các hàm SQLXML

  3. sử dụng cơ sở dữ liệu nosql, như HBase


3
Chắc chắn là một mô hình chống cho hầu hết các trường hợp sử dụng. Nếu bạn có một tập dữ liệu thực sự nhỏ và hiệu suất không thành vấn đề thì nó có thể phù hợp với bạn.
JNK

16

Trong PostgreSQL, một cách rất tốt để xử lý các cấu trúc EAV là mô-đun bổ sung hstore, có sẵn cho phiên bản 8.4 trở lên. Tôi trích dẫn hướng dẫn:

Mô-đun này thực hiện hstorekiểu dữ liệu để lưu trữ các cặp khóa / giá trị trong một giá trị PostgreQuery duy nhất. Điều này có thể hữu ích trong các tình huống khác nhau, chẳng hạn như các hàng có nhiều thuộc tính hiếm khi được kiểm tra hoặc dữ liệu bán cấu trúc. Khóa và giá trị chỉ đơn giản là chuỗi văn bản.

Vì Postgres 9.2 cũng có jsonloại và một loạt các chức năng đi kèm với nó ( hầu hết được thêm vào với 9.3 ).

Postgres 9.4 thêm kiểu dữ liệu "nhị phân JSON" (phần lớn vượt trội!) jsonbVào danh sách các tùy chọn. Với các tùy chọn chỉ mục nâng cao.


10

Nếu bạn có một cơ sở dữ liệu đang sử dụng cấu trúc EAV, có thể truy vấn dữ liệu theo nhiều cách khác nhau.

Câu trả lời của @ Simon đã chỉ ra cách thực hiện một truy vấn bằng nhiều phép nối.

Dữ liệu mẫu được sử dụng:

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

Nếu bạn đang sử dụng RDBMS có PIVOTchức năng ( SQL Server 2005+ / Oracle 11g + ) thì bạn có thể truy vấn dữ liệu theo cách sau:

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;

Xem SQL Fiddle với bản demo

Nếu bạn không có quyền truy cập vào một PIVOThàm, thì bạn có thể sử dụng hàm tổng hợp với một CASEcâu lệnh để trả về dữ liệu:

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

Xem SQL Fiddle với bản demo

Cả hai truy vấn này sẽ trả về dữ liệu trong kết quả:

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

10

Thật buồn cười khi thấy mô hình db EAV bị chỉ trích và thậm chí được coi là "chống mẫu" bởi một số người.

Theo tôi thấy, nhược điểm chính là:

  • Đường cong học tập sẽ dốc hơn nếu bạn tham gia vào một dự án đã bắt đầu sử dụng EAV trước đây. Thật vậy, các truy vấn rất khó khăn khi bạn tăng đáng kể số lượng tham gia (và bảng) và do đó nó sẽ yêu cầu bạn có thêm thời gian để hiểu. Chỉ cần nhìn vào dự án Magento và xem làm thế nào các nhà phát triển bên ngoài dự án có một thời gian khó khăn khi làm việc với DB, nhưng tài liệu này vẫn được duy trì tốt.
  • Không phù hợp để báo cáo , nếu bạn cần lấy số lượng người bắt đầu bằng "M", v.v ...

Tuy nhiên, bạn chắc chắn không nên loại bỏ giải pháp này và đây là lý do:

  • Simon đã nói về con quái vật gọi là "thay đổi yêu cầu". Tôi thích biểu hiện này :). Và IMHO đây chính là lý do tại sao EAV có thể là một ứng cử viên tốt, bởi vì điều này rất phù hợp với "thay đổi" , vì bạn có thể thêm nhiều thuộc tính như bạn muốn khá dễ dàng. Tất nhiên nó phụ thuộc vào yêu cầu chúng ta thay đổi. Nếu chúng ta đang nói về một doanh nghiệp hoàn toàn mới, tất nhiên bạn sẽ phải xem lại dataModel của mình, nhưng EAV cung cấp rất nhiều tính linh hoạt. Chỉ vì nó yêu cầu sự khắt khe hơn, không có nghĩa là điều này ít thú vị hơn.
  • Người ta cũng nói rằng "Bạn không thể sử dụng các loại dữ liệu." : Điều này là sai . Bạn rất có thể có một vài bảng giá trị , một bảng cho mỗi kiểu dữ liệu. Sau đó, bạn phải xác định trong bảng thuộc tính của mình loại dữ liệu nào là thuộc tính của bạn. Trong thực tế, sự pha trộn giữa mối quan hệ / EAV cổ điển với mối quan hệ lớp cung cấp rất nhiều tiềm năng thú vị trong thiết kế dataBase.

2
Đường cong học tập dốc hơn cho lần gặp gỡ thiết kế EAV đầu tiên. Sau đó, tất cả trông giống nhau.
ypercubeᵀᴹ

1
Nhận xét tạm thời: Tôi không hiểu tại sao yêu cầu "không phù hợp để báo cáo". EAV có vẻ tuyệt vời để báo cáo. Chọn ObjectId từ eav.values ​​trong đó propertyId = name và value như 'm%'. Các thay đổi đối với lược đồ ảo (ví dụ: thêm thuộc tính) có thể được bao gồm trong bất kỳ giao diện báo cáo động nào (như danh sách thả xuống) mà không phải biên dịch lại.
crokusek
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.