5

假设我们有一个应该能够存储各种产品的应用程序。每个产品至少有一个ID和一个Name,但所有其他属性都可以由用户自己定义。

  1. 例如,他可以创建一个产品组Ipods,其中包含属性容量
  2. 例如,他可以创建具有属性sizecolor的产品组 TShirts
  3. 我们需要存储产品的定义和具体产品本身。
  4. 我们希望确保可以轻松地按产品属性进行聚合 (GROUP BY)。例如选择每一代ipods的总容量
  5. 该解决方案不得要求架构更改(由于 Bill Karwin 的输入而增加了要求 - 也请参阅他的答案!)

您将如何根据上述要求为您的模式建模?

注意:要求 4. 很重要!

感谢大家贡献和讨论该方法。过去我已经看到了一些解决这个问题的方法,但没有一个让我对分组变得容易:(

4

5 回答 5

11

我推荐具体表继承类表继承设计。两种设计都满足您的所有四个标准。

在具体表继承中:

  1. Ipod 存储在具有, , ,product_ipods列的表中。IDNameCapacityGeneration
  2. T 恤存储在带有, , ,product_tshirts列的表中。IDNameSizeColor
  3. 具体产品类型的定义在 和 的元数据(表定义)product_ipodsproduct_tshirts
  4. SELECT SUM(Capacity) FROM product_ipods GROUP BY Generation;

在类表继承中:

  1. 通用产品属性存储在Products具有列ID、的表中Name

    Ipod 存储在product_ipods带有列product_id(外键到Products.ID)、Capacity、的表中Generation

  2. Tshirts 存储在product_tshirts带有列的表中product_id(外键到Products.ID)、SizeColor

  3. 具体产品类型的定义在 、 和 的元数据(表定义)products中。product_ipodsproduct_tshirts

  4. SELECT SUM(Capacity) FROM product_ipods GROUP BY Generation;


另请参阅我对“产品表,多种产品,每种产品都有许多参数”的回答,其中我针对您所描述的问题类型描述了几种解决方案。我还详细说明了EAV 为何是一个损坏的设计。


来自@dcolumbus 的重新评论:

使用 CTI,product_ipods 的每一行都会是它自己的价格的变化吗?

products如果每种类型的产品都有价格,我希望价格列出现在表格中。对于 CTI,产品类型表通常只包含仅与该类型产品相关的属性列。所有产品类型共有的任何属性都会在父表中获取列。

此外,在存储订单行项目时,您是否会将 product_ipods 中的行存储为行项目?

products在 line-items 表中,存储产品 id,它在表和表中应该是相同的值product_ipods


来自@dcolumbus 的评论:

这对我来说似乎是多余的......在那种情况下,我没有看到子表的意义。但即使子表确实有意义,连接是id什么?

子表的重点是存储所有其他产品类型不需要的列。

连接 id 可以是一个自动递增的数字。子类型表不需要自动增加自己的id,因为它可以使用超级表生成的值。

CREATE TABLE products (
  product_id INT AUTO_INCREMENT PRIMARY KEY,
  sku VARCHAR(30) NOT NULL,
  name VARCHAR(100) NOT NULL,
  price NUMERIC(9,2) NOT NULL
);

CREATE TABLE product_ipods (
  product_id INT PRIMARY KEY,
  size TINYINT DEFAULT 16,
  color VARCHAR(10) DEFAULT 'silver',
  FOREIGN KEY (product_id) REFERENCES products(product_id)
);

INSERT INTO products (sku, name, price) VALUES ('IPODS1C1', 'iPod Touch', 229.00);
INSERT INTO product_ipods VALUES (LAST_INSERT_ID(), 16, 'silver');
INSERT INTO products (sku, name, price) VALUES ('IPODS1C2', 'iPod Touch', 229.00);
INSERT INTO product_ipods VALUES (LAST_INSERT_ID(), 16, 'black');
INSERT INTO products (sku, name, price) VALUES ('IPODS1C3', 'iPod Touch', 229.00);
INSERT INTO product_ipods VALUES (LAST_INSERT_ID(), 16, 'red');
INSERT INTO products (sku, name, price) VALUES ('IPODS2C1', 'iPod Touch', 299.00);
INSERT INTO product_ipods VALUES (LAST_INSERT_ID(), 32, 'silver');
INSERT INTO products (sku, name, price) VALUES ('IPODS2C2', 'iPod Touch', 299.00);
INSERT INTO product_ipods VALUES (LAST_INSERT_ID(), 32, 'silver');
INSERT INTO products (sku, name, price) VALUES ('IPODS2C3', 'iPod Touch', 299.00);
INSERT INTO product_ipods VALUES (LAST_INSERT_ID(), 32, 'red');
于 2009-05-19T18:16:51.387 回答
5

分组并不容易,因为您将在“颜色”上使用什么聚合运算符?请注意,不能在案例 2 上使用您的要求 4。

在任何情况下,聚合只是因为数据类型的变化而变得困难,并且可以通过以更类型安全的方式接近它来缓解 - 知道添加苹果和橙子永远没有意义。

这是经典的 EAV 模型,它在精心设计的数据库中占有一席之地。为了使它更安全一点,我已经看到了将值存储在类型安全表中而不是单个自由格式 varchar 列中的情况。

而不是价值观:

EntityID int
,AttributeID int
,Value varchar(255)

您有多个表:

EntityID int
,AttributeID int
,ValueMoney money

EntityID int
,AttributeID int
,ValueInt int

etc.

然后获取每代 iPod 容量:

SELECT vG.ValueVarChar AS Generation, SUM(vC.ValueDecimal) AS TotalCapacity
FROM Products AS p
INNER JOIN Attributes AS aG
    ON aG.AttributeName = 'generation'
INNER JOIN ValueVarChar AS vG
    ON vG.EntityID = p.ProductID
    AND vG.AttributeID = aG.AttributeID
INNER JOIN Attributes AS aC
    ON aC.AttributeName = 'capacity'
INNER JOIN ValueDecimal AS vC
    ON vC.EntityID = p.ProductID
    AND vC.AttributeID = aC.AttributeID
GROUP BY vG.ValueVarChar
于 2009-05-19T17:50:54.300 回答
0

通过在用户执行操作时添加/删除列来为新产品创建表和更改表。使用模式来了解每个产品具有哪些属性。这满足了您的所有四个要求。

您还需要一个表来存储其他表名称或在表前加上您可以针对表的 sysobjects 查询的内容:

select [name] from sysobjects where [name] like 'product_%' AND xtype='U'
于 2009-05-19T17:37:23.850 回答
0

听起来您正在寻找设计产品目录数据库。

我推荐这种方法。 http://edocs.bea.com/wlp/docs40/catalog/schemcat.htm

于 2009-05-19T17:42:50.003 回答
0

我想知道如何克服使用 BLOB 模式代替 EAV 的问题。假设我们可以将实体的所有自定义字段作为字符串存储在一个字段中,例如在 JSON 中类似于 tihis: {customField1: value1, customField2: value2, ..., customFieldN: valueN}

如何克服以下问题: 1. 如何通过单独的自定义字段进行搜索,例如查找条件为 custField1 = value1 AND customField2 = value2 的实体?2.如何保持数据完整性,例如,如果我们删除实体的自定义字段,如何删除实体中这些自定义字段的所有值。

于 2010-11-16T07:43:28.763 回答