2

我正在尝试找出零售商的数据模型。

该零售商在全国有几家商店,它们使用以下层次结构建模:

Channel -> Zone -> City -> Store

每个商店都包含几篇文章。每篇文章都有类似的属性

  • 激活标志(这表明文章的存在)
  • 价格
  • 供应商
  • 仓库

现在,零售商可以在层次结构中的任何级别设置这些属性。考虑以下情况:

  • 在渠道级别为文章设置价格将应用于所有商店。
  • 设置在更高级别的价格可以在任何其他级别被覆盖。例如,在城市级别仅针对城市中的商店或针对特定商店。
  • 这适用于上面列出的所有属性。

到目前为止,他们已经使用 RDBMS 对其进行建模,方法是在层次结构的顶部定义全局规则并将异常单独调用为单独的行。比如说,价格表将在频道级别为文章设置价格,并且任何级别的任何更改都将单独指定。显然,这在获取存储级别的属性时效率不高。

样本数据

假设 Channel、Zone、City 和 Store 统称为实体。Channel 的 id 范围 >= 4000,Zone >= 3000,City >= 2000,stores 范围从 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 的价格。

希望这可以让您了解当前模型。那么,您能否建议一种更好的存储方式?

4

1 回答 1

2

在关系模型中表示树和层次结构的方法很少——搜索 SO 会返回很多答案。

该模型的主要思想是用闭包表和层次级别来表示层次结构。所以,Level表有(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);
于 2012-11-17T23:49:18.577 回答