Danh sách thiết kế danh sách thuộc tính sản phẩm


9

Tôi đang làm việc để cập nhật cơ sở dữ liệu sản phẩm của trang web của chúng tôi. Nó được xây dựng trong MySQL nhưng đây là một câu hỏi mẫu thiết kế cơ sở dữ liệu chung.

Tôi đang lên kế hoạch chuyển sang mô hình Supertype / Subtype. Cơ sở dữ liệu hiện tại / trước đây của chúng tôi chủ yếu là một bảng duy nhất có dữ liệu về một loại sản phẩm. Chúng tôi đang xem xét mở rộng cung cấp sản phẩm của chúng tôi để bao gồm các sản phẩm khác nhau.

Thiết kế dự thảo mới này là như thế này:

Product             product_[type]          product_attribute_[name]
----------------    ----------------        ----------------------------
part_number (PK)    part_number (FK)        attributeId (PK)
UPC                 specific_attr1 (FK)     attribute_name
price               specific_attr2 (FK)
...                 ...

Tôi có một câu hỏi liên quan đến các bảng thuộc tính sản phẩm. Ý tưởng ở đây là một sản phẩm có thể có một danh sách các thuộc tính nhất định như màu sắc: đỏ, xanh lá cây, xanh dương hoặc vật liệu: nhựa, gỗ, crôm, nhôm, v.v.

Danh sách này sẽ được lưu trữ trong một bảng và khóa chính (PK) cho mục thuộc tính đó sẽ được sử dụng trong bảng sản phẩm cụ thể làm khóa ngoại (FK).

(Cuốn sách Các mẫu kiến ​​trúc ứng dụng doanh nghiệp của Martin Fowler gọi đây là " Ánh xạ khóa ngoài ")

Điều này cho phép giao diện trang web kéo danh sách các thuộc tính cho một loại thuộc tính nhất định và nhổ nó ra trong menu chọn thả xuống hoặc một số thành phần UI khác. Danh sách này có thể được coi là danh sách "được ủy quyền" của các giá trị thuộc tính.

Số lượng tham gia kết thúc xảy ra khi kéo một sản phẩm cụ thể xuất hiện quá mức đối với tôi. Bạn phải tham gia mọi bảng thuộc tính sản phẩm cho sản phẩm để bạn có thể nhận được các trường thuộc tính đó. Thông thường, trường đó có thể chỉ đơn giản là không có gì nhiều hơn một chuỗi (varchar) cho tên của nó.

Mẫu thiết kế này kết thúc việc tạo ra một số lượng lớn các bảng cũng như bạn kết thúc với một bảng cho mỗi thuộc tính. Một ý tưởng để chống lại điều này sẽ là tạo ra một cái gì đó nhiều hơn của một bảng lấy túi của Khăn cho tất cả các thuộc tính sản phẩm. Một cái gì đó như thế này:

product_attribute
----------------
attributeId (PK) 
name
field_name

Bằng cách này, bảng của bạn có thể trông như thế này:

1  red     color
2  blue    color
3  chrome  material
4  plastic material
5  yellow  color
6  x-large size

Điều này có thể giúp giảm creep bảng nhưng nó không làm giảm số lượng tham gia và cảm thấy hơi sai khi kết hợp rất nhiều loại khác nhau vào một bảng. Nhưng bạn có thể dễ dàng có được tất cả các thuộc tính màu sắc có sẵn của Wap.

Tuy nhiên, có thể có một thuộc tính có nhiều trường hơn chỉ là "tên", chẳng hạn như giá trị RGB của màu. Điều này sẽ yêu cầu thuộc tính cụ thể đó có thể có một bảng khác hoặc có một trường duy nhất cho cặp tên: giá trị (có nhược điểm riêng của nó).

Mẫu thiết kế cuối cùng tôi có thể nghĩ đến là lưu trữ giá trị thuộc tính thực tế trong bảng sản phẩm cụ thể và hoàn toàn không có bảng thuộc tính. Một cái gì đó như thế này:

Product             product_[type] 
----------------    ----------------
part_number (PK)    part_number (FK) 
UPC                 specific_attr1 
price               specific_attr2 
...                 ...

Thay vì Khóa ngoài cho bảng khác, nó sẽ chứa giá trị thực như:

part_number    color    material
-----------    -----    --------
1234           red      plastic

Điều này sẽ loại bỏ sự tham gia và ngăn chặn creep bảng (có thể?). Tuy nhiên, điều này ngăn cản việc có một danh sách được ủy quyền của người dùng. Bạn có thể trả về tất cả các giá trị được nhập hiện tại cho một trường nhất định (ví dụ: màu sắc) nhưng điều này cũng loại bỏ ý tưởng về việc có một danh sách được ủy quyền của các giá trị đối với một thuộc tính nhất định.

Để có danh sách đó, bạn sẽ vẫn phải tạo bảng thuộc tính của nhóm Grab lấy túi hoặc có nhiều bảng (creep bảng) cho mỗi thuộc tính.

Điều này tạo ra nhược điểm lớn hơn (và tại sao tôi chưa bao giờ sử dụng phương pháp này) hiện có tên sản phẩm ở nhiều địa điểm.

Nếu bạn có giá trị màu của khăn đỏ và trong bảng thuộc tính của chủ sở hữu, và cũng lưu nó trong bảng sản phẩm [loại], một bản cập nhật cho bảng chủ Master sẽ gây ra sự cố toàn vẹn dữ liệu nếu ứng dụng không Cũng không cập nhật tất cả các bản ghi với giá trị cũ trong bảng sản phẩm_type '.

Vì vậy, sau khi tôi giải thích và phân tích dài dòng về kịch bản này, tôi nhận ra rằng đây không phải là một kịch bản hiếm gặp và thậm chí có thể đặt tên cho loại tình huống này.

Có những giải pháp được chấp nhận chung cho thách thức thiết kế này? Là số lượng lớn các phép nối có thể chấp nhận được nếu các bảng tương đối nhỏ? Việc lưu trữ tên thuộc tính, thay vì PK thuộc tính có thể được chấp nhận trong một số trường hợp không? Có giải pháp nào khác mà tôi không nghĩ tới không?

Một vài lưu ý về cơ sở dữ liệu / ứng dụng sản phẩm này:

  • Sản phẩm không được cập nhật / thêm / xóa thường xuyên
  • Các thuộc tính không được cập nhật / thêm / xóa thường xuyên
  • Bảng được truy vấn thường xuyên nhất để đọc / trả lại thông tin
  • Bộ nhớ đệm phía máy chủ được bật để lưu trữ kết quả của một truy vấn / kết quả đã cho
  • Tôi dự định bắt đầu chỉ với một loại sản phẩm và mở rộng / thêm các loại khác theo thời gian và sẽ có khả năng hơn 10 loại khác nhau

1
Bạn sẽ có bao nhiêu loại sản phẩm?
dezso

1
Câu hỏi hay. Nó sẽ bắt đầu nhỏ 3-4 nhưng phát triển lớn hơn tới 10+
jmbertucci

Bạn có ý nghĩa gì bởi "Danh sách các thuộc tính được ủy quyền"?
NoChance

Xin lỗi, nó phải là "giá trị thuộc tính". Ý tưởng rằng bạn có một bảng liệt kê tất cả các giá trị được phép cho một thuộc tính. I E. Dưới đây là danh sách 10 màu mà loại sản phẩm này có thể. 10 giá trị này là giá trị "ủy quyền" mà một số người có thể chọn.
jmbertucci

Tôi tự hỏi liệu có ổn không khi tất cả các giá trị thuộc tính này được nối vào bảng loại sản phẩm nếu cuối cùng tôi tạo ra "chế độ xem" trên đầu trang?
jmbertucci

Câu trả lời:


17

Cá nhân tôi sẽ sử dụng một mô hình tương tự như sau:

Bảng sản phẩm sẽ khá cơ bản, chi tiết sản phẩm chính của bạn:

create table product
(
  part_number int, (PK)
  name varchar(10),
  price int
);
insert into product values
(1, 'product1', 50),
(2, 'product2', 95.99);

Thứ hai bảng thuộc tính để lưu trữ từng thuộc tính khác nhau.

create table attribute
(
  attributeid int, (PK)
  attribute_name varchar(10),
  attribute_value varchar(50)
);
insert into attribute values
(1, 'color', 'red'),
(2, 'color', 'blue'),
(3, 'material', 'chrome'),
(4, 'material', 'plastic'),
(5, 'color', 'yellow'),
(6, 'size', 'x-large');

Cuối cùng tạo bảng Product_attribution dưới dạng bảng THAM GIA giữa mỗi sản phẩm và các thuộc tính của nó được liên kết với nó.

create table product_attribute
(
  part_number int, (FK)
  attributeid int  (FK) 
);
insert into product_attribute values
(1,  1),
(1,  3),
(2,  6),
(2,  2),
(2,  6);

Tùy thuộc vào cách bạn muốn sử dụng dữ liệu bạn đang xem hai liên kết:

select *
from product p
left join product_attribute t
  on p.part_number = t.part_number
left join attribute a
  on t.attributeid = a.attributeid;

Xem SQL Fiddle với Demo . Điều này trả về dữ liệu ở định dạng:

PART_NUMBER | NAME       | PRICE | ATTRIBUTEID | ATTRIBUTE_NAME | ATTRIBUTE_VALUE
___________________________________________________________________________
1           | product1   | 50    | 1           | color          | red
1           | product1   | 50    | 3           | material       | chrome
2           | product2   | 96    | 6           | size           | x-large
2           | product2   | 96    | 2           | color          | blue
2           | product2   | 96    | 6           | size           | x-large

Nhưng nếu bạn muốn trả về dữ liệu ở PIVOTđịnh dạng mà bạn có một hàng với tất cả các thuộc tính dưới dạng cột, bạn có thể sử dụng các CASEcâu lệnh có tổng hợp:

SELECT p.part_number,
  p.name,
  p.price,
  MAX(IF(a.ATTRIBUTE_NAME = 'color', a.ATTRIBUTE_VALUE, null)) as color,
  MAX(IF(a.ATTRIBUTE_NAME = 'material', a.ATTRIBUTE_VALUE, null)) as material,
  MAX(IF(a.ATTRIBUTE_NAME = 'size', a.ATTRIBUTE_VALUE, null)) as size
from product p
left join product_attribute t
  on p.part_number = t.part_number
left join attribute a
  on t.attributeid = a.attributeid
group by p.part_number, p.name, p.price;

Xem SQL Fiddle với Demo . Dữ liệu được trả về ở định dạng:

PART_NUMBER | NAME       | PRICE | COLOR | MATERIAL | SIZE
_________________________________________________________________
1           | product1   | 50    | red   | chrome   | null
2           | product2   | 96    | blue  | null     | x-large

Như trường hợp bạn thấy dữ liệu có thể ở định dạng tốt hơn cho bạn, nhưng nếu bạn có một số thuộc tính không xác định, nó sẽ dễ dàng trở nên không thể kiểm soát được do tên thuộc tính mã hóa cứng, vì vậy, trong MySQL, bạn có thể sử dụng các câu lệnh được chuẩn bị để tạo ra các pivots động . Mã của bạn sẽ như sau (Xem SQL Fiddle With Demo ):

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'MAX(IF(a.attribute_name = ''',
      attribute_name,
      ''', a.attribute_value, NULL)) AS ',
      attribute_name
    )
  ) INTO @sql
FROM attribute;

SET @sql = CONCAT('SELECT p.part_number
                    , p.name
                    , ', @sql, ' 
                   from product p
                   left join product_attribute t
                     on p.part_number = t.part_number
                   left join attribute a
                     on t.attributeid = a.attributeid
                   GROUP BY p.part_number
                    , p.name');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

Điều này tạo ra kết quả tương tự như phiên bản thứ hai mà không cần mã hóa bất cứ thứ gì. Trong khi có nhiều cách để mô hình hóa điều này, tôi nghĩ rằng thiết kế cơ sở dữ liệu này là linh hoạt nhất.


+1 - Một câu trả lời tuyệt vời bằng văn bản. Tôi vẫn dành một chút thời gian để đọc lại và tiêu hóa câu trả lời này trước khi chấp nhận. Nó trông giống như một giải pháp tốt cho câu hỏi của tôi về các phép nối và thuộc tính sản phẩm và thậm chí vượt lên trên và với các ví dụ về pivots và các câu lệnh được chuẩn bị. Vì vậy, tôi sẽ bắt đầu với +1 cho điều đó. =)
jmbertucci

@jmbertucci bạn có vẻ lo lắng về việc truy vấn các bảng vì vậy tôi đoán rằng tôi sẽ cung cấp cho bạn một số mẫu. :)
Taryn

Thật. Tôi sẽ "làm" mà tôi không thấy làm một bảng chéo sản phẩm cho thuộc tính. Có lẽ là một trường hợp suy nghĩ quá mức đặc biệt là sau khi đắm chìm các mẫu thiết kế và lý thuyết. Ngoài ra, trải nghiệm DBA của tôi là cơ bản và làm nhiều hơn với các tuyên bố đã chuẩn bị là điều tôi cần, vì vậy sự bao gồm của bạn là hữu ích nhất. Và câu trả lời này đã giúp phá vỡ "khối nhà văn" mà tôi đang có để tôi có thể tiếp tục với dự án này, điều làm nên ngày của tôi. =)
jmbertucci

tốt, một câu hỏi ... nó có chậm không? Tôi cảm thấy như bạn sẽ mất hơn 30 giây để chỉ truy vấn 10
nghìn

@ZenithS Bạn sẽ phải kiểm tra nó để xem và có thể thêm các chỉ mục trên các cột bạn truy vấn. Tôi không có ví dụ MySQL để thực hiện bất kỳ thử nghiệm nào.
Taryn

0

Tôi sẽ mở rộng câu trả lời của Taryn và sửa đổi bảng thuộc tính để có cột fk_attribution_type_id sẽ thay cho cột property_name và trỏ đến bảng property_type mới.

Vì vậy, bạn có các loại thuộc tính có cấu trúc trong một bảng và bạn có thể thay đổi nó bất cứ lúc nào ở một nơi.

Theo ý kiến ​​của tôi, tốt hơn là làm việc với loại "quay số" (bảng với các loại có thể) so với loại enum (như cột thuộc tính_name (và trên đó thực sự không phải là tên, loại thuộc tính của nó)).

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.