我正在试图找出零售商的数据模型。
零售商在全国各地有几家商店,他们使用以下层次建模:
Channel -> Zone -> City -> Store
每家商店都包含多篇文章。每篇文章都有像
这样的属性现在,零售商可以在层次结构中的任何级别设置这些属性。请考虑以下情况:
截至目前,他们已使用RDBMS对其进行建模,方法是在层次结构顶部定义全局规则,并将异常分别作为单独的行调用。比如,价格表,将为渠道级别的文章设置价格,并且将单独指定任何级别的任何更改。显然,在商店级别获取属性时效率不高。
示例数据
假设频道,区域,城市和商店统称为实体。频道的ID范围> = 4000,区域> = 3000,城市> = 2000,存储范围从0到1000.
层次关系数据的子集如下:
Channel | Zone | City | Store |
----------+----------+------------------
4001 | 3001 | 2001 | 13 |
4001 | 3001 | 2001 | 14 |
4001 | 3001 | 2002 | 15 |
4001 | 3002 | 2003 | 16 |
4001 | 3003 | 2006 | 74 |
价格表
ArticleID | EntityID | Price
----------+----------+----------
12345 | 4001 | 2.5
12345 | 2003 | 2.9
12345 | 74 | 3.0
此处,将为所有商店中的商品设置渠道4001的价格2.5。接下来的两行设置了某些商店的价格例外。根据上述层级关系,为城市2003设定的第二个价格2.9仅适用于商店16中的商品。并且第三行直接为商店74中的商品设定价格3.0。
希望这给出了当前模型的概念。所以,请你建议一个更好的存储方式吗?
答案 0 :(得分:2)
在关系模型中表示树和层次结构的方法很少 - 搜索SO将返回相当多的答案。
此模型的主要思想是使用闭包表和层次结构级表示层次结构。
因此,级别表格为(1, Channel) , (2, Zone) , (3, City) , (4, Store)
。
闭包表公开了节点的每个节点和所有后代。重要的是要注意每个节点也是它自己的后代。
第一个CTE查询(q_00)选择节点的价格并将其分配给所有后代。 LevelDiff 列计算后代节点中有多少级别是价格指定节点。
由于价格可能会在多个级别指定,因此最终查询会选择在最低级别 LevelDiff 级别指定的商店价格。
语法为PostgeSQL
,但也应该很容易转换为其他语法。
with q_00 as (
select
a.ProductID
, c.LocationID
, c.LocationLevel
, a.Price
, t.DescendantLocationID
, t.DescendantLevel
, (t.DescendantLevel - c.LocationLevel ) as LevelDiff
from ProductPrice as a
join Product as b on b.ProductId = a.ProductID
join Location as c on c.LocationID = a.LocationID
join TreeClosure as t on t.LocationID = c.LocationID
)
select
a.ProductID
, DescendantLocationID
, Price
from q_00 as a
join Level as w on w.LevelNo = a.DescendantLevel
where w.LevelName = 'Store'
and a.Leveldiff = (select min(LevelDiff)
from q_00 as x
where x.DescendantLocationID = a.DescendantLocationID
and x.ProductID = a.ProductID ) ;
总而言之,这是一个测试结果,定价定义为:
Channel=1, Product=1, Price = 11.0
Channel=1, City=111, Product=1, Price = 11.5
Channel=1, City=111, Store =1112, Product=1, Price = 12.0
查询返回(参见下面的测试数据)
ProductID | DescendantLocationID | PriceID
-----------------------------------------
1 1231 11.00
1 1232 11.00
1 1111 11.50
1 1112 12.00
这是DDL(PosgreSQL)
CREATE TABLE Level (
LevelNo integer NOT NULL ,
LevelName varchar(20) NOT NULL
);
ALTER TABLE Level ADD CONSTRAINT XPKLevel PRIMARY KEY (LevelNo) ;
CREATE TABLE Location (
LocationID integer NOT NULL ,
LocationLevel integer NOT NULL
);
ALTER TABLE Location ADD CONSTRAINT XPKLocation PRIMARY KEY (LocationID);
ALTER TABLE Location ADD CONSTRAINT XAK1Location UNIQUE (LocationID, LocationLevel) ;
CREATE TABLE Product (
ProductID integer NOT NULL
);
ALTER TABLE Product ADD CONSTRAINT XPKProduct PRIMARY KEY (ProductID);
CREATE TABLE ProductPrice (
ProductID integer NOT NULL ,
LocationID integer NOT NULL ,
Price decimal(19,2) NOT NULL
);
ALTER TABLE ProductPrice ADD CONSTRAINT XPKProductPrice PRIMARY KEY (ProductID, LocationID);
CREATE TABLE ProductSupplier (
ProductID integer NOT NULL ,
LocationID integer NOT NULL ,
SupplierID integer NOT NULL
);
ALTER TABLE ProductSupplier ADD CONSTRAINT XPKProductSupplier PRIMARY KEY (ProductID, LocationID);
CREATE TABLE Supplier (
SupplierID integer NOT NULL
);
ALTER TABLE Supplier ADD CONSTRAINT XPKSupplier PRIMARY KEY (SupplierID) ;
CREATE TABLE TreeClosure (
LocationID integer NOT NULL ,
DescendantLocationID integer NOT NULL ,
DescendantLevel integer NOT NULL
);
ALTER TABLE TreeClosure ADD CONSTRAINT XPKTreeClosure PRIMARY KEY (LocationID, DescendantLocationID);
ALTER TABLE Location
ADD CONSTRAINT FK1_Location FOREIGN KEY (LocationLevel) REFERENCES Level(LevelNo);
ALTER TABLE ProductPrice
ADD CONSTRAINT FK1_ProductPrice FOREIGN KEY (ProductID) REFERENCES Product(ProductID);
ALTER TABLE ProductPrice
ADD CONSTRAINT FK2_ProductPrice FOREIGN KEY (LocationID) REFERENCES Location(LocationID);
ALTER TABLE ProductSupplier
ADD CONSTRAINT FK1_PrdSup FOREIGN KEY (ProductID) REFERENCES Product(ProductID);
ALTER TABLE ProductSupplier
ADD CONSTRAINT FK2_PrdSup FOREIGN KEY (SupplierID) REFERENCES Supplier(SupplierID);
ALTER TABLE ProductSupplier
ADD CONSTRAINT FK3_PrdSup FOREIGN KEY (LocationID) REFERENCES Location(LocationID);
ALTER TABLE TreeClosure
ADD CONSTRAINT FK1_TC FOREIGN KEY (LocationID) REFERENCES Location(LocationID);
ALTER TABLE TreeClosure
ADD CONSTRAINT FK2_TC FOREIGN KEY (DescendantLocationID,DescendantLevel) REFERENCES Location(LocationID,LocationLevel);
用
测试一些数据insert into Level (LevelNo, LevelName)
values
(1, 'Channel')
, (2, 'Zone')
, (3, 'City')
, (4, 'Store')
;
insert into Product (ProductID)
values (1) , (2) , (3)
;
-- Locations
insert into Location (LocationID, LocationLevel)
values
(1, 1)
, (11, 2)
, (111, 3)
, (1111, 4)
, (1112, 4)
, (12, 2)
, (123, 3)
, (1231, 4)
, (1232, 4)
;
-- Tree closure (hierarchy)
insert into TreeClosure (LocationID, DescendantLocationID, DescendantLevel)
values
(1 , 1 , 1)
, (1 , 11 , 2)
, (1 , 111 , 3)
, (1 , 1111, 4)
, (1 , 1112, 4)
, (11 , 11 , 2)
, (11 , 111 , 3)
, (11 , 1111, 4)
, (11 , 1112, 4)
, (111 , 111 , 3)
, (111 , 1111, 4)
, (111 , 1112, 4)
, (1111, 1111, 4)
, (1112, 1112, 4)
--
, (1 , 12 , 2)
, (1 , 123 , 3)
, (1 , 1231, 4)
, (1 , 1232, 4)
, (12 , 12 , 2)
, (12 , 123 , 3)
, (12 , 1231, 4)
, (12 , 1232, 4)
, (123 , 123, 3)
, (123 , 1231, 4)
, (123 , 1232, 4)
, (1231, 1231, 4)
, (1232, 1232, 4)
;
-- pricing
insert into ProductPrice (ProductID, LocationID, Price) values (1, 1 , 11.0);
insert into ProductPrice (ProductID, LocationID, Price) values (1, 111 , 11.5);
insert into ProductPrice (ProductID, LocationID, Price) values (1, 1112, 12.0);