Mic*_*hal 5 sql sql-server schema database-design entity-attribute-value
假设我们有一个应该能够存储所有类型产品的应用程序.每个产品至少具有一个ID和一个Name但是所有其他属性可以由用户自己定义.
您如何根据上述要求对模式进行建模?
注意:要求4.很重要!
感谢大家的贡献和讨论方法.我在过去看到过这个问题的一些解决方案,但没有一个能让我轻松分组:(
Bil*_*win 11
我建议使用Concrete Table Inheritance或Class Table Inheritance设计.两种设计都满足您的所有四个标准.
在具体表继承中:
product_ipods的列ID,Name,Capacity,Generation.product_tshirts的列ID,Name,Size,Color.product_ipods和product_tshirts.SELECT SUM(Capacity) FROM product_ipods GROUP BY Generation;在类表继承中:
通用产品属性存储在Products具有列的表ID中Name.
iPod的存储在表中product_ipods的列product_id(外键Products.ID)Capacity,Generation.
product_tshirts的列product_id(外键Products.ID)Size,Color.products,product_ipods和product_tshirts.SELECT SUM(Capacity) FROM product_ipods GROUP BY Generation;另请参阅我对" 产品表,多种产品,每种产品都有许多参数 "的回答,其中我描述了您所描述的问题类型的几种解决方案.我还详细介绍了为什么EAV是一个破碎的设计.
来自@dcolumbus的评论:
对于CTI,product_ipods的每一行都是一个变量,它的价格是多少?
products如果每种类型的产品都有价格,我希望价格列出现在表格中.对于CTI,产品类型表通常只包含仅适用于该类型产品的属性列.所有产品类型共有的任何属性都会获得父表中的列.
此外,在存储订单行项目时,您是否会将product_ipods中的行存储为订单项?
在行项目表中,存储产品ID,该值在products表和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');
Run Code Online (Sandbox Code Playgroud)
分组不会很容易,因为您将在“颜色”上使用什么聚合运算符?请注意,不可能在案例 2 上使用您的要求 4。
在任何情况下,聚合只是由于数据类型的变化而变得困难,并且可以通过以更类型安全的方式接近它来减轻聚合 - 知道添加苹果和橙子永远没有意义。
这是经典的 EAV 模型,它在精心设计的数据库中占有一席之地。为了使其更加类型安全,我见过将值存储在类型安全表中而不是单个自由格式 varchar 列中的情况。
而不是价值观:
EntityID int
,AttributeID int
,Value varchar(255)
Run Code Online (Sandbox Code Playgroud)
您有多个表:
EntityID int
,AttributeID int
,ValueMoney money
EntityID int
,AttributeID int
,ValueInt int
etc.
Run Code Online (Sandbox Code Playgroud)
然后获取每代 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
Run Code Online (Sandbox Code Playgroud)