Cách thiết kế bảng sản phẩm cho nhiều loại sản phẩm trong đó mỗi sản phẩm có nhiều tham số


140

Tôi không có nhiều kinh nghiệm trong thiết kế bảng. Mục tiêu của tôi là tạo ra một hoặc nhiều bảng sản phẩm đáp ứng các yêu cầu dưới đây:

  • Hỗ trợ nhiều loại sản phẩm (TV, Điện thoại, PC, ...). Mỗi loại sản phẩm có một bộ thông số khác nhau, như:

    • Điện thoại sẽ có Màu sắc, Kích thước, Trọng lượng, HĐH ...

    • PC sẽ có CPU, HDD, RAM ...

  • Tập hợp các tham số phải là động. Bạn có thể thêm hoặc chỉnh sửa bất kỳ tham số nào bạn thích.

Làm cách nào tôi có thể đáp ứng các yêu cầu này mà không có bảng riêng cho từng loại sản phẩm?

Câu trả lời:


233

Bạn có ít nhất năm tùy chọn này để mô hình hóa phân cấp loại bạn mô tả:

  • Kế thừa bảng đơn : một bảng cho tất cả các loại Sản phẩm, với đủ các cột để lưu trữ tất cả các thuộc tính của tất cả các loại. Điều này có nghĩa là rất nhiều cột, hầu hết trong số đó là NULL trên bất kỳ hàng nào.

  • Kế thừa bảng lớp : một bảng cho Sản phẩm, lưu trữ các thuộc tính chung cho tất cả các loại sản phẩm. Sau đó, một bảng cho mỗi loại sản phẩm, lưu trữ các thuộc tính cụ thể cho loại sản phẩm đó.

  • Kế thừa bảng bê tông : không có bảng cho các thuộc tính Sản phẩm phổ biến. Thay vào đó, một bảng cho mỗi loại sản phẩm, lưu trữ cả thuộc tính sản phẩm phổ biến và thuộc tính dành riêng cho sản phẩm.

  • LOB nối tiếp : Một bảng cho Sản phẩm, lưu trữ các thuộc tính chung cho tất cả các loại sản phẩm. Một cột thêm lưu trữ BLOB dữ liệu bán cấu trúc, theo định dạng XML, YAML, JSON hoặc một số định dạng khác. BLOB này cho phép bạn lưu trữ các thuộc tính cụ thể cho từng loại sản phẩm. Bạn có thể sử dụng Mẫu thiết kế lạ mắt để mô tả điều này, chẳng hạn như Mặt tiền và Memento. Nhưng bất kể bạn có một loạt các thuộc tính không thể truy vấn dễ dàng trong SQL; bạn phải tìm nạp toàn bộ blob trở lại ứng dụng và sắp xếp nó ra khỏi đó.

  • Thực thể-Thuộc tính-Giá trị : Một bảng cho Sản phẩm và một bảng xoay các thuộc tính cho các hàng, thay vì các cột. EAV không phải là một thiết kế hợp lệ đối với mô hình quan hệ, nhưng nhiều người vẫn sử dụng nó. Đây là "Mẫu thuộc tính" được đề cập bởi một câu trả lời khác. Xem các câu hỏi khác với thẻ eav trên StackOverflow để biết một số cạm bẫy.

Tôi đã viết thêm về điều này trong một bài thuyết trình, Mô hình dữ liệu mở rộng .


Những suy nghĩ bổ sung về EAV: Mặc dù nhiều người dường như ủng hộ EAV, nhưng tôi thì không. Nó có vẻ như là giải pháp linh hoạt nhất, và do đó là tốt nhất. Tuy nhiên, hãy ghi nhớ câu ngạn ngữ TANSTAAFL . Dưới đây là một số nhược điểm của EAV:

  • Không có cách nào để tạo một cột bắt buộc (tương đương NOT NULL).
  • Không có cách nào để sử dụng các kiểu dữ liệu SQL để xác nhận các mục.
  • Không có cách nào để đảm bảo rằng tên thuộc tính được đánh vần nhất quán.
  • Không có cách nào để đặt khóa ngoại vào các giá trị của bất kỳ thuộc tính đã cho nào, ví dụ: đối với bảng tra cứu.
  • Tìm nạp kết quả trong bố cục dạng bảng thông thường rất phức tạp và tốn kém, vì để có được các thuộc tính từ nhiều hàng bạn cần thực hiện JOINcho từng thuộc tính.

Mức độ linh hoạt mà EAV mang lại cho bạn đòi hỏi sự hy sinh trong các lĩnh vực khác, có thể làm cho mã của bạn phức tạp (hoặc tệ hơn) so với việc giải quyết vấn đề ban đầu theo cách thông thường hơn.

Và trong hầu hết các trường hợp, không cần thiết phải có mức độ linh hoạt đó. Trong câu hỏi của OP về các loại sản phẩm, việc tạo bảng cho mỗi loại sản phẩm cho các thuộc tính dành riêng cho sản phẩm sẽ đơn giản hơn rất nhiều, do đó bạn có một số cấu trúc nhất quán được thi hành ít nhất cho các mục cùng loại sản phẩm.

Tôi chỉ sử dụng EAV nếu mỗi hàng phải được phép có khả năng có một bộ thuộc tính riêng biệt. Khi bạn có một bộ các loại sản phẩm hữu hạn, EAV là quá mức cần thiết. Kế thừa bảng lớp sẽ là lựa chọn đầu tiên của tôi.


Cập nhật 2019: Tôi càng thấy mọi người sử dụng JSON như một giải pháp cho vấn đề "nhiều thuộc tính tùy chỉnh", tôi càng không thích giải pháp đó. Nó làm cho các truy vấn quá phức tạp, ngay cả khi sử dụng các hàm JSON đặc biệt để hỗ trợ chúng. Phải mất nhiều không gian lưu trữ hơn để lưu trữ tài liệu JSON, so với việc lưu trữ trong các hàng và cột thông thường.

Về cơ bản, không có giải pháp nào trong số này là dễ dàng hoặc hiệu quả trong cơ sở dữ liệu quan hệ. Toàn bộ ý tưởng về việc có "các thuộc tính biến đổi" về cơ bản là mâu thuẫn với lý thuyết quan hệ.

Những gì nó được đưa ra là bạn phải chọn một trong những giải pháp dựa trên đó là ít xấu nhất cho ứng dụng của bạn . Do đó, bạn cần biết bạn sẽ truy vấn dữ liệu như thế nào trước khi bạn chọn thiết kế cơ sở dữ liệu. Không có cách nào để chọn một giải pháp "tốt nhất" bởi vì bất kỳ giải pháp nào cũng có thể là tốt nhất cho một ứng dụng nhất định.


11
@HimalayaGarg Tùy chọn "4.5" thực sự trái ngược với toàn bộ quan điểm của bài viết của Bill.
3308043

2
Không giống như MySQL, SQL Server có hỗ trợ rộng rãi cho XML, XPath và XQuery. Vì vậy, đối với người dùng SQL Server, tùy chọn tốt nhất sẽ là lưu trữ các thuộc tính bổ sung trong một cột có kiểu XML (tùy chọn 4). Bằng cách này, bạn KHÔNG phải "lấy toàn bộ blob trở lại ứng dụng và sắp xếp nó ra khỏi đó." Bạn thậm chí có thể tạo các chỉ mục trên các cột XML trong SQL Server.
Delphi.Boy


2
Tôi thích LOB nối tiếp cho trường hợp của tôi. Nhưng nó có phù hợp với ORM không? Tôi sử dụng EF.
Mahmood Jenami 22/03/2015

@ user2741577, chắc chắn, nhưng có lẽ bạn sẽ phải viết mã tùy chỉnh để giải nén các trường dữ liệu phi cấu trúc của LOB và áp dụng chúng cho từng trường thực thể của đối tượng ORM của bạn. Tôi không biết EF, nhưng tôi cho rằng bạn có thể tạo một lớp ORM cơ sở thực hiện điều này. Bạn cần theo dõi trường nào đến từ các trường cụ thể của hàng cơ sở dữ liệu và trường nào đến từ các trường của LOB, để bạn có thể tạo lại LOB khi đến lúc lưu đối tượng.
Bill Karwin

12

@Trái tim sắt đá

Tôi sẽ đến đây với EAV và MVC mọi cách.

@Bill Karvin

Dưới đây là một số nhược điểm của EAV:

  • Không có cách nào để tạo một cột bắt buộc (tương đương với KHÔNG NULL).
  • Không có cách nào để sử dụng các kiểu dữ liệu SQL để xác nhận các mục.
  • Không có cách nào để đảm bảo rằng tên thuộc tính được đánh vần nhất quán.
  • Không có cách nào để đặt khóa ngoại vào các giá trị của bất kỳ thuộc tính đã cho nào, ví dụ: đối với bảng tra cứu.

Tất cả những điều mà bạn đã đề cập ở đây:

  • xác nhận dữ liệu
  • xác nhận tên thuộc tính
  • cột / trường bắt buộc
  • xử lý việc phá hủy các thuộc tính phụ thuộc

theo ý kiến ​​của tôi hoàn toàn không thuộc về cơ sở dữ liệu vì không có cơ sở dữ liệu nào có khả năng xử lý các tương tác và yêu cầu đó ở mức độ phù hợp như ngôn ngữ lập trình của ứng dụng.

Theo tôi, sử dụng cơ sở dữ liệu theo cách này cũng giống như sử dụng đá để đóng đinh. Bạn có thể làm điều đó với một hòn đá nhưng bạn không cho rằng sử dụng búa được thiết kế chính xác và đặc biệt hơn cho loại hoạt động này?

Tìm nạp kết quả trong bố cục dạng bảng thông thường rất phức tạp và tốn kém, vì để có được các thuộc tính từ nhiều hàng, bạn cần phải THAM GIA cho từng thuộc tính.

Vấn đề này có thể được giải quyết bằng cách thực hiện một vài truy vấn trên dữ liệu một phần và xử lý chúng thành bố cục dạng bảng với ứng dụng của bạn. Ngay cả khi bạn có 600GB dữ liệu sản phẩm, bạn có thể xử lý theo lô nếu bạn yêu cầu dữ liệu từ mỗi hàng trong bảng này.

Đi xa hơn Nếu bạn muốn cải thiện hiệu suất của các truy vấn, bạn có thể chọn một số thao tác nhất định, ví dụ như báo cáo hoặc tìm kiếm văn bản toàn cầu và chuẩn bị cho chúng các bảng chỉ mục sẽ lưu trữ dữ liệu cần thiết và sẽ được tạo lại theo định kỳ, cứ sau 30 phút.

Bạn thậm chí không cần phải quan tâm đến chi phí lưu trữ dữ liệu thêm bởi vì nó ngày càng rẻ hơn mỗi ngày.

Nếu bạn vẫn quan tâm đến hiệu suất của các hoạt động được thực hiện bởi ứng dụng, bạn luôn có thể sử dụng Erlang, C ++, Go Language để xử lý trước dữ liệu và sau đó chỉ xử lý dữ liệu được tối ưu hóa hơn nữa trong ứng dụng chính của bạn.


you can always use Erlang, C++, Go Language to pre-process the dataBạn có ý gì? Thay vì DB, sử dụng Go lang? Bạn có thể vui lòng giải thích về điều đó?
Green

1
Tôi hoàn toàn đồng ý. EAV là một cách để đi, đặc biệt nếu bạn cần mức độ linh hoạt cho phép bạn thêm loại sản phẩm và tham số mới mà không thay đổi lược đồ db, ý tôi là sống trong sản xuất thông qua ứng dụng của bạn. Đã từng trải qua rồi. Đã làm cho tôi. Về các truy vấn chậm ... có ai ở đây đã từng nghe về cache chưa? ;)
pawel.kalisz

@Green Tôi đã chỉnh sửa đoạn cuối để làm cho rõ ràng hơn, nhưng đó là về việc chuyển dữ liệu EAV thô của bạn sang một quy trình bằng ngôn ngữ có thể xử lý các biến đổi dữ liệu, tra cứu trong cấu trúc cây hoặc bất kỳ bản đồ cơ bản nào làm giảm hoạt động thực sự nhanh chóng và một cách hiệu quả bộ nhớ. Các chi tiết cụ thể ở đây sẽ phụ thuộc vào những gì cần được tối ưu hóa
Pawel Barcik 7/2/2017

6

Nếu tôi sử dụng Class Table Inheritanceý nghĩa:

một bảng cho Sản phẩm, lưu trữ các thuộc tính chung cho tất cả các loại sản phẩm. Sau đó, một bảng cho mỗi loại sản phẩm, lưu trữ các thuộc tính cụ thể cho loại sản phẩm đó. -Bill Karwin

Mà tôi thích những gợi ý hay nhất của Bill Karwin .. Tôi có thể thấy trước một nhược điểm, tôi sẽ cố gắng giải thích làm thế nào để không trở thành vấn đề.

Tôi nên có kế hoạch dự phòng nào khi một thuộc tính chỉ phổ biến cho 1 loại, sau đó trở thành phổ biến cho 2, sau đó 3, v.v.?

Ví dụ: (đây chỉ là một ví dụ, không phải vấn đề thực sự của tôi)

Nếu chúng tôi bán đồ nội thất, chúng tôi có thể bán ghế, đèn, ghế sofa, TV, v.v ... Loại TV có thể là loại duy nhất chúng tôi mang theo có mức tiêu thụ điện. Vì vậy, tôi sẽ đặt power_consumptionthuộc tính trên tv_type_table. Nhưng sau đó chúng tôi bắt đầu thực hiện các hệ thống rạp hát tại nhà cũng có một power_consumptiontài sản. OK nó chỉ là một sản phẩm khác vì vậy tôi cũng sẽ thêm lĩnh vực này stereo_type_tablevì đó có lẽ là dễ nhất vào thời điểm này. Nhưng theo thời gian khi chúng ta bắt đầu mang theo ngày càng nhiều thiết bị điện tử, chúng tôi nhận ra rằng power_consumptionnó đủ rộng để nó có trong main_product_table. Tôi nên làm gì bây giờ?

Thêm trường vào main_product_table. Viết một kịch bản để lặp qua các thiết bị điện tử và đặt giá trị chính xác từ mỗi type_tableđến main_product_table. Sau đó thả cột đó từ mỗi type_table.

Bây giờ Nếu tôi luôn sử dụng cùng một GetProductDatalớp để tương tác với cơ sở dữ liệu để lấy thông tin sản phẩm; sau đó nếu bây giờ có bất kỳ thay đổi nào trong mã cần tái cấu trúc, thì chúng chỉ nên đến Class đó.


3

Bạn có thể có một bảng Sản phẩm và một bảng ProductAdditionInfo riêng biệt với 3 cột: ID sản phẩm, tên thông tin bổ sung, giá trị thông tin bổ sung. Nếu màu sắc được nhiều người sử dụng nhưng không phải tất cả các loại Sản phẩm, bạn có thể có một cột không thể có trong bảng Sản phẩm hoặc chỉ đặt nó trong ProductAdditableInfo.

Cách tiếp cận này không phải là một kỹ thuật truyền thống cho cơ sở dữ liệu quan hệ, nhưng tôi đã thấy nó được sử dụng rất nhiều trong thực tế. Nó có thể linh hoạt và có hiệu suất tốt.

Steve Yegge gọi đây là mẫu Thuộc tính và đã viết một bài đăng dài về việc sử dụng nó.


4
Mẫu thuộc tính chỉ là Thực thể-Thuộc tính-Giá trị theo tên khác. Nó được sử dụng rộng rãi, nhưng lưu trữ nó trong cơ sở dữ liệu quan hệ phá vỡ các quy tắc chuẩn hóa.
Bill Karwin

2
Thành thật mà nói, khi tôi đọc mô tả về EAV trong câu trả lời @Bills, tôi hoàn toàn không hiểu anh ấy đang giải thích điều gì. Nhưng khi bạn nói 3 columns: product ID, additional info name, additional info valuetôi hiểu khái niệm. Và tôi đã thực sự làm điều này trước đây, và gặp vấn đề. Tuy nhiên, hiện tại tôi không nhớ những vấn đề đó là gì.
JD Isaacks

1
@JDIsaacks Trong mô hình này, một vấn đề phổ biến là chúng ta không biết chúng ta cần bao nhiêu THAM GIA để tìm nạp tất cả các thuộc tính.
Omid
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.