you*_*rrr 20 database-design subtypes
首先,这里是我建议的架构示例,供我在整个帖子中参考:
Clothes
----------
ClothesID (PK) INT NOT NULL
Name VARCHAR(50) NOT NULL
Color VARCHAR(50) NOT NULL
Price DECIMAL(5,2) NOT NULL
BrandID INT NOT NULL
...
Brand_1
--------
ClothesID (FK/PK) int NOT NULL
ViewingUrl VARCHAR(50) NOT NULL
SomeOtherBrand1SpecificAttr VARCHAR(50) NOT NULL
Brand_2
--------
ClothesID (FK/PK) int NOT NULL
PhotoUrl VARCHAR(50) NOT NULL
SomeOtherBrand2SpecificAttr VARCHAR(50) NOT NULL
Brand_X
--------
ClothesID (FK/PK) int NOT NULL
SomeOtherBrandXSpecificAttr VARCHAR(50) NOT NULL
Run Code Online (Sandbox Code Playgroud)
我有一个衣服表,其中包含名称、颜色、价格、品牌标识等列来描述特定服装项目的属性。
这是我的问题:不同品牌的服装需要不同的信息。处理此类问题的最佳做法是什么?
请注意,出于我的目的,有必要从服装条目开始查找特定于品牌的信息。这是因为我首先将衣服条目的信息显示给用户,然后我必须使用其特定于品牌的信息来购买该项目。总之,衣服(来自)和brand_x表之间必须存在方向关系。
为了应对这种情况,我想到了以下设计方案:
的衣服表将具有品牌柱可具有的ID值范围为1至x,其中一个特定的ID对应于一个特定品牌的表。例如,id 值 1 将对应表brand_1(可能有一个url列),id 2 将对应于brand_2(可能有一个供应商列)等。
因此,要将特定的服装条目与其品牌特定信息相关联,我想应用程序级别的逻辑将如下所示:
Clothes
----------
ClothesID (PK) INT NOT NULL
Name VARCHAR(50) NOT NULL
Color VARCHAR(50) NOT NULL
Price DECIMAL(5,2) NOT NULL
BrandID INT NOT NULL
...
Brand_1
--------
ClothesID (FK/PK) int NOT NULL
ViewingUrl VARCHAR(50) NOT NULL
SomeOtherBrand1SpecificAttr VARCHAR(50) NOT NULL
Brand_2
--------
ClothesID (FK/PK) int NOT NULL
PhotoUrl VARCHAR(50) NOT NULL
SomeOtherBrand2SpecificAttr VARCHAR(50) NOT NULL
Brand_X
--------
ClothesID (FK/PK) int NOT NULL
SomeOtherBrandXSpecificAttr VARCHAR(50) NOT NULL
Run Code Online (Sandbox Code Playgroud)
我正在尝试在 BCNF 中规范化我的整个数据库,虽然这是我想出的,但产生的应用程序代码让我感到非常焦虑。除了在应用程序级别之外,没有办法强制执行关系,因此设计感觉非常hacky,并且我预计非常容易出错。
在发帖之前,我一定要查看以前的条目。这是我设法找到的一个几乎相同问题的帖子。无论如何我发了这篇文章,因为似乎提供的唯一答案没有 SQL 或基于设计的解决方案(即它提到了 OOP、继承和接口)。
在数据库设计方面,我也是一个新手,所以我很感激任何见解。
Stack Overflow 上似乎有更多有用的回复:
我已经提到了那里的解决方案,并建议其他发现我的问题的人也这样做。
尽管提供了上述链接,但我仍在寻找此处的回复,并希望提供任何解决方案!
我正在使用 PostgreSQL。
我个人不喜欢为此使用多表模式。
我已经设置了一个 dbfiddle示例。
我建议的表架构:
CREATE TABLE #Brands
(
BrandId int NOT NULL PRIMARY KEY,
BrandName nvarchar(100) NOT NULL
);
CREATE TABLE #Clothes
(
ClothesId int NOT NULL PRIMARY KEY,
ClothesName nvarchar(100) NOT NULL
);
-- Lookup table for known attributes
--
CREATE TABLE #Attributes
(
AttrId int NOT NULL PRIMARY KEY,
AttrName nvarchar(100) NOT NULL
);
-- holds common propeties, url, price, etc.
--
CREATE TABLE #BrandsClothes
(
BrandId int NOT NULL REFERENCES #Brands(BrandId),
ClothesId int NOT NULL REFERENCES #Clothes(ClothesId),
VievingUrl nvarchar(300) NOT NULL,
Price money NOT NULL,
PRIMARY KEY CLUSTERED (BrandId, ClothesId),
INDEX IX_BrandsClothes NONCLUSTERED (ClothesId, BrandId)
);
-- holds specific and unlimited attributes
--
CREATE TABLE #BCAttributes
(
BrandId int NOT NULL REFERENCES #Brands(BrandId),
ClothesId int NOT NULL REFERENCES #Clothes(ClothesId),
AttrId int NOT NULL REFERENCES #Attributes(AttrId),
AttrValue nvarchar(300) NOT NULL,
PRIMARY KEY CLUSTERED (BrandId, ClothesId, AttrId),
INDEX IX_BCAttributes NONCLUSTERED (ClothesId, BrandId, AttrId)
);
Run Code Online (Sandbox Code Playgroud)
让我插入一些数据:
INSERT INTO #Brands VALUES
(1, 'Brand1'), (2, 'Brand2');
INSERT INTO #Clothes VALUES
(1, 'Pants'), (2, 'T-Shirt');
INSERT INTO #Attributes VALUES
(1, 'Color'), (2, 'Size'), (3, 'Shape'), (4, 'Provider'), (0, 'Custom');
INSERT INTO #BrandsClothes VALUES
(1, 1, 'http://mysite.com?B=1&C=1', 123.99),
(1, 2, 'http://mysite.com?B=1&C=2', 110.99),
(2, 1, 'http://mysite.com?B=2&C=1', 75.99),
(2, 2, 'http://mysite.com?B=2&C=2', 85.99);
INSERT INTO #BCAttributes VALUES
(1, 1, 1, 'Blue, Red, White'),
(1, 1, 2, '32, 33, 34'),
(1, 2, 1, 'Pearl, Black widow'),
(1, 2, 2, 'M, L, XL'),
(2, 1, 4, 'Levis, G-Star, Armani'),
(2, 1, 3, 'Slim fit, Regular fit, Custom fit'),
(2, 2, 4, 'G-Star, Armani'),
(2, 2, 3, 'Slim fit, Regular fit'),
(2, 2, 0, '15% Discount');
Run Code Online (Sandbox Code Playgroud)
如果您需要获取公共属性:
SELECT b.BrandName, c.ClothesName, bc.VievingUrl, bc.Price
FROM #BrandsClothes bc
INNER JOIN #Brands b
ON b.BrandId = bc.BrandId
INNER JOIN #Clothes c
ON c.ClothesId = bc.ClothesId
ORDER BY bc.BrandId, bc.ClothesId;
BrandName ClothesName VievingUrl Price
--------- ----------- ------------------------- ------
Brand1 Pants http://mysite.com?B=1&C=1 123.99
Brand1 T-Shirt http://mysite.com?B=1&C=2 110.99
Brand2 Pants http://mysite.com?B=2&C=1 75.99
Brand2 T-Shirt http://mysite.com?B=2&C=2 85.99
Run Code Online (Sandbox Code Playgroud)
或者您可以轻松获得品牌服装:
给我所有Brand2的衣服
SELECT c.ClothesName, b.BrandName, a.AttrName, bca.AttrValue
FROM #BCAttributes bca
INNER JOIN #BrandsClothes bc
ON bc.BrandId = bca.BrandId
AND bc.ClothesId = bca.ClothesId
INNER JOIN #Brands b
ON b.BrandId = bc.BrandId
INNER JOIN #Clothes c
ON c.ClothesId = bc.ClothesId
INNER JOIN #Attributes a
ON a.AttrId = bca.AttrId
WHERE bca.ClothesId = 2
ORDER BY bca.ClothesId, bca.BrandId, bca.AttrId;
ClothesName BrandName AttrName AttrValue
----------- --------- -------- ---------------------
T-Shirt Brand1 Color Pearl, Black widow
T-Shirt Brand1 Size M, L, XL
T-Shirt Brand2 Custom 15% Discount
T-Shirt Brand2 Shape Slim fit, Regular fit
T-Shirt Brand2 Provider G-Star, Armani
Run Code Online (Sandbox Code Playgroud)
但对我来说,这种模式的最好之处之一是您可以按属性进行过滤:
给我所有具有以下属性的衣服:尺寸
SELECT c.ClothesName, b.BrandName, a.AttrName, bca.AttrValue
FROM #BCAttributes bca
INNER JOIN #BrandsClothes bc
ON bc.BrandId = bca.BrandId
AND bc.ClothesId = bca.ClothesId
INNER JOIN #Brands b
ON b.BrandId = bc.BrandId
INNER JOIN #Clothes c
ON c.ClothesId = bc.ClothesId
INNER JOIN #Attributes a
ON a.AttrId = bca.AttrId
WHERE bca.AttrId = 2
ORDER BY bca.ClothesId, bca.BrandId, bca.AttrId;
ClothesName BrandName AttrName AttrValue
----------- --------- -------- ----------
Pants Brand1 Size 32, 33, 34
T-Shirt Brand1 Size M, L, XL
Run Code Online (Sandbox Code Playgroud)
使用多表模式,无论前面的任何查询都需要处理无限数量的表,或者处理 XML 或 JSON 字段。
此架构的另一个选项是您可以定义模板,例如,您可以添加一个新表 BrandAttrTemplates。每次添加新记录时,您都可以使用触发器或 SP 为该分支生成一组预定义的属性。
对不起,我想扩展我的解释,我认为它比我的英语更清楚。
更新
无论哪个 RDBMS,我当前的答案都应该适用。根据您的评论,如果您需要过滤属性值,我建议进行一些小的更改。
就 MS-Sql 不允许数组而言,我已经设置了一个包含相同表架构的新示例,但将 AttrValue 更改为 ARRAY 字段类型。
事实上,使用 POSTGRES,您可以使用 GIN 索引来利用这个数组。
(让我说@EvanCarrol 对 Postgres 有很好的了解,肯定比我好。但让我补充一点。)
CREATE TABLE BCAttributes
(
BrandId int NOT NULL REFERENCES Brands(BrandId),
ClothesId int NOT NULL REFERENCES Clothes(ClothesId),
AttrId int NOT NULL REFERENCES Attrib(AttrId),
AttrValue text[],
PRIMARY KEY (BrandId, ClothesId, AttrId)
);
CREATE INDEX ix_attributes on BCAttributes(ClothesId, BrandId, AttrId);
CREATE INDEX ix_gin_attributes on BCAttributes using GIN (AttrValue);
INSERT INTO BCAttributes VALUES
(1, 1, 1, '{Blue, Red, White}'),
(1, 1, 2, '{32, 33, 34}'),
(1, 2, 1, '{Pearl, Black widow}'),
(1, 2, 2, '{M, L, XL}'),
(2, 1, 4, '{Levis, G-Star, Armani}'),
(2, 1, 3, '{Slim fit, Regular fit, Custom fit}'),
(2, 2, 4, '{G-Star, Armani}'),
(2, 2, 3, '{Slim fit, Regular fit}'),
(2, 2, 0, '{15% Discount}');
Run Code Online (Sandbox Code Playgroud)
现在,您还可以使用单独的属性值进行查询,例如:
给我一份所有裤子的清单 尺码:33
AttribId = 2 AND ARRAY['33'] && bca.AttrValue
SELECT c.ClothesName, b.BrandName, a.AttrName, array_to_string(bca.AttrValue, ', ')
FROM BCAttributes bca
INNER JOIN BrandsClothes bc
ON bc.BrandId = bca.BrandId
AND bc.ClothesId = bca.ClothesId
INNER JOIN Brands b
ON b.BrandId = bc.BrandId
INNER JOIN Clothes c
ON c.ClothesId = bc.ClothesId
INNER JOIN Attrib a
ON a.AttrId = bca.AttrId
WHERE bca.AttrId = 2
AND ARRAY['33'] && bca.AttrValue
ORDER BY bca.ClothesId, bca.BrandId, bca.AttrId;
Run Code Online (Sandbox Code Playgroud)
这是结果:
clothes name | brand name | attribute | values
------------- ------------ ---------- ----------------
Pants Brand1 Size 32, 33, 34
Run Code Online (Sandbox Code Playgroud)