如何改善这种无模式的数据库架构?

时间:2018-08-11 09:51:19

标签: mysql database-design relational-database

在进入模式和表之前,我想分享一下我首先要实现的目标。我正在开发一种快递应用程序,其中有一些categories,每个类别都有一个预定义的price

但是,确定价格有点丑陋(没有对称性和图案;至少,我似乎找不到任何东西)。我给你举个例子:

请考虑以下类别:文档,重型文档,笔记本电脑,纸箱,重型纸箱。

1)文档:适用于重量小于0.5kg的较浅的文档。价格是20美元,固定的。

[价格表中存储的价格:20.00]

  

例如一件300克的价格为20美元。

2)较重的文件:这适用于重量超过0.5kg的文件。与“文档”类别不同,它没有固定价格!相反,它有一个单位价格:每公斤10美元,除0.5公斤/0.5公斤以后,它将应用于每公斤。

[价格表中存储的价格:10.00]

  

例如对于2千克的物品,价格为35美元(1.5克= 15美元+ 0.5 = 20美元)

3)笔记本电脑:简单明了,100美元。没什么特别的,没有任何约束。

[价格表中存储的价格:100.00]

  

例如对于2千克的物品,价格为35美元(1.5克= 15美元+ 0.5 = 20美元)

4)纸箱:有一个有趣的例子。到目前为止,只有一个依赖项:weight。但这又有一个依赖性:dimension。这有点类似于文档类别。对于低于3立方英尺(CF)的纸箱,价格为每CF 80美元。单据和纸箱类别之间的区别在于,单据具有固定价格,而纸箱具有单价。但是,等等,还有更多。还有一个额外的约束:尺寸重量比。在这种情况下,它是7kg per CF。如果商品的重量超过比例,则每增加1公斤,就要收取5美元。我知道这太令人困惑了。一个示例可能会有所帮助:

[价格表中存储的价格:80.00]

  

例如纸箱80公斤和2CF;价格将为490 $。方法如下:

首先计算常规费用:80 $ * 2CF = 160 $ 现在,让我们找出它是否穿过 Ratio :由于1 CF = 7kg,因此2CF = 14kg。但是该商品的重量为80kg,因此它超过了比例(14kg)

由于它超出了比率,因此,对于所有额外的千克(80-14 = 66千克),每千克将花费5美元:66 * 5 = 330美元。加上常规费用后:330 $ + 160 $ = 490 $。

5)重型纸箱:该纸箱用于尺寸大于3CF的纸箱。与纸箱的区别是单价。重型纸箱每CF为60美元。

[价格表中存储的价格:60.00]

  

例如纸箱80公斤和5CF;价格将为525 $。方法如下:

首先计算常规费用:60 $ * 5CF = 300 $ 现在,让我们找出它是否穿过 Ratio :由于1 CF = 7kg,因此5CF = 35kg。但是该商品的重量为80kg,所以它超过了35kg的比例

由于它超出了比率,因此,对于所有额外的千克(80-35 = 45千克),每千克将花费5美元:45 * 5 = 225美元。加上常规费用后:300 $ + 225 $ = 325 $。

如果您已经阅读了到目前为止,我想我已经说服了您,业务结构确实很复杂。现在,让我们看一下我的categories模式:

+-------------------------+---------------------------------+------+-----+---------+----------------+
| Field                   | Type                            | Null | Key | Default | Extra          |
+-------------------------+---------------------------------+------+-----+---------+----------------+
| id                      | int(10) unsigned                | NO   | PRI | NULL    | auto_increment |
| name                    | varchar(191)                    | NO   |     | NULL    |                |
| created_at              | timestamp                       | YES  |     | NULL    |                |
| updated_at              | timestamp                       | YES  |     | NULL    |                |
| dim_dependency          | tinyint(1)                      | NO   |     | NULL    |                |
| weight_dependency       | tinyint(1)                      | NO   |     | NULL    |                |
| distance_dependency     | tinyint(1)                      | NO   |     | NULL    |                |
| dim_weight_ratio        | varchar(191)                    | YES  |     | NULL    |                |
| constraint_value        | decimal(8,2)                    | YES  |     | NULL    |                |
| constraint_on           | enum('weight','dim')            | YES  |     | NULL    |                |
| size                    | enum('short','regular','large') | YES  |     | regular |                |
| over_ratio_price_per_kg | decimal(8,2)                    | YES  |     | NULL    |                |
| deleted_at              | timestamp                       | YES  |     | NULL    |                |
+-------------------------+---------------------------------+------+-----+---------+----------------+

还有prices表的架构(这是一个多态表,希望有朝一日创建一个subcategories表):

+----------------+---------------------+------+-----+---------+----------------+
| Field          | Type                | Null | Key | Default | Extra          |
+----------------+---------------------+------+-----+---------+----------------+
| id             | int(10) unsigned    | NO   | PRI | NULL    | auto_increment |
| amount         | decimal(8,2)        | NO   |     | NULL    |                |
| created_at     | timestamp           | YES  |     | NULL    |                |
| updated_at     | timestamp           | YES  |     | NULL    |                |
| priceable_type | varchar(191)        | NO   | MUL | NULL    |                |
| priceable_id   | bigint(20) unsigned | NO   |     | NULL    |                |
| deleted_at     | timestamp           | YES  |     | NULL    |                |
+----------------+---------------------+------+-----+---------+----------------+

我如何改善这种结构以保持事物的动态性和连贯性?

1 个答案:

答案 0 :(得分:1)

因此,如果我要发送10kg/2.99cf包裹,则要向我收取240$3*80$)的纸箱费用。如果我将那个包裹放在稍大的盒子里,现在想以10kg/3.01cf包裹的形式发送,则您要向我收取180$3*60$)的重纸箱费用。如果您舍入到下一个完整的cf,假设我要发送一个80kg/3CF数据包;您向我收取535$3*80+59*5)。如果我将相同的包装放在带有80kg/4CF的较大包装盒中,那么您只需向我收取500$4*60+52*5)。

这确实是一个好兆头“业务结构真的很复杂” (即使这些仅仅是示例值,也显示出使事情变得过于复杂的潜力)。

无论如何,我可能会在这样的表中编码您的条件:

category |max_kg|max_cf|is_laptop|price|p_p_kg|p_p_cf|off_kg|off_cf|off_rat
---------+------+------+---------+-----+------+------+------+------+--------
Document | 0.5  | null |    0    |  20 |   0  |   0  |  0   |  0   |  0     
Heavy Doc|  2   | null |    0    |  20 |  10  |   0  | 0.5  |  0   |  0   
Laptop   | null | null |    1    | 100 |   0  |   0  |  0   |  0   |  0  
Carton   | null |   3  |    0    |   0 |   5  |  80  |  0   |  0   |  7  
Heavy C. | null | null |    0    | 180 |   5  |  60  |  0   |  3   |  7  

文档也可能有一些尺寸限制(例如,我可以将0.0kg/100cf充氦气的气球作为文档发送吗?),但您尚未指定它们;列出条件,这样可以使您在没有特定条件的地方显而易见。

off_*指定偏移量,例如price中已包含的金额; p_p_kg是剩余重量(减去偏移量)的每公斤价格 ,类似于p_p_cf。因此,带有80kg/4CF的厚纸箱将被计算为

price    -- 180
+ p_p_kg * greatest(kg - off_rat * cf - off_kg, 0)  -- 5 * (80-7*4-0) 
+ p_p_cf * greatest(cf - off_cf, 0) -- 60 * (4 - 3)

所以,180 + 5 * 52 + 60 = 500是预期的。

用户不会来您的商店说​​“ 我想作为重型纸箱发送” 。他会说:“如果我发送重量为80公斤,3 cf且没有笔记本电脑的东西,我要花多少钱。” 他可能会期望如果重的话,您不要将其作为纸箱发送纸箱会便宜些。

因此,您需要使用此输入(以及其他任何相关输入,例如距离),并使用

来检查满足条件的所有行。
select (your price formula depending on input) as cost
...
where (max_kg is null or max_kg >= 80) 
  and (max_cf is null or max_cf >= 3)
  and (is_laptop is null or is_laptop = 0)
order by cost

您可能应该在一个地方定义它,因此添加表中未定义的其他条件(例如,区别)和其他规范(例如,四舍五入为完整的cf或0.1步是更容易的)。

您可能还需要一张带有其他服务的桌子,例如快递或隔夜送达,500美元以上包裹的保险,固定时间或类似地点的送达服务。

您提到了子类别和“多态价格表”,但尚不清楚要使用它做什么。如果您有一些无法在像这样的矩阵表中表述的具体示例,请添加它们。但是,您还应该意识到,无论是对于您还是对客户而言,简单性都是至上的。如果您认为我的240$纸箱收了我的钱10kg/2.99cf,如果您实际上是向我收取200$的钱,那么您可能已经迷失了我。装一个重纸箱。