6

注意:此问题已于 2012 年 11 月 19 日重新表述以进行澄清。我通常在这里没有太多问题,但在为客户站点设计新产品系统时遇到了困难。我们提供一套产品,每位客户都可以出售给他的客户。我们可能随时添加新产品,但它们都遵循以下格式:

  1. 类别
  2. 类型
  3. 产品

举一个使用之前结构的真实示例:

  • 棒球装备
    • 手套
      • 罗林斯
      • 耐克
      • 美津浓
    • 蝙蝠
      • 伊斯顿
      • 路易斯维尔重击手
  • 足球装备
      • 耐克
      • 锐步
      • 阿迪达斯
    • 足球
      • 耐克
      • 树苗
      • 威尔逊
  • ……

上面的列表显然还在继续,可能会大得多,但它给出了整体思路。

目前,我将特定客户可以销售的产品类型存储在一个单一的平面格式表中,如下所示:

ID  | clientID | categoryID | typeID | productID | customURL
=============================================================
1   |  111     |    1       |   1    |   1       | 1111
2   |  111     |    1       |   2    |   2       | 2222
3   |  111     |    1       |   2    |   3       | 3333
4   |  111     |    2       |   3    |   4       | 4444
5   |  222     |    1       |   1    |   1       | 5555
6   |  222     |    2       |   3    |   4       | 6666
  • 在上面的示例中,类别 1 可以是“棒球装备”,类别 2 是“足球装备”
  • 对应的 categoryID、typeID 和 productID 的名称将存储在具有 FK 关系的 3 个单独的表中(innodb),以保持规范化。
  • 类型指的是二级物品(手套、球棒、鞋子、足球等)。这些数字永远不会相交(这意味着即使一般产品相同,也永远不会有相同的 typeID(棒球鞋的 ID 与足球鞋的 ID 不同)。
  • 在此表中,clientID 1 可以销售 4 个产品,3 个在类别 1,1 个在类别 2。ClientID 2 可以销售 2 个产品,每个类别一个。

我倾向于使表格保持结构化,但知道在其他设计中我可能出于标准化目的将表格分开,我不确定是否适用于此。如果我把它们分开,我会看到这从一张桌子变成了 4 张或更多,如下所示:

产品提供表

ID  | clientID | productID | customURL
======================================
1   |  111     | 1       | 1111
2   |  111     | 2       | 2222
3   |  111     | 3       | 3333
4   |  111     | 4       | 4444
5   |  222     | 1       | 5555
6   |  222     | 4       | 6666

产品定义表

ID  | productID | typeID | productName
======================================
1   |  1        |    1   | rawlings glove
2   |  2        |    2   | product2
3   |  3        |    2   | product3
4   |  4        |    3   | product4

类型定义表

ID  | typeID | categoryID | typeName
=====================================
1   |  1     |    1       | Gloves
2   |  2     |    1       | Bats
3   |  3     |    2       | Shoes
4   |  4     |    2       | Footballs

类别定义表

ID  | categoryID | catName
=============================
1   |  1         | Baseball Equipment
2   |  2         | Football Equipment

我是不是想太多了?两种方法不是以相同的方式获得最终解决方案吗(后者只涉及几个连接来收集平面表,如图 1 所示)?

4

4 回答 4

9

归一化的目的和好处是它使输入异常数据变得更加困难(理想情况下是不可能的)。

例如,在您的图 1 中,什么可以防止您意外存储 typeid 3 和 categoryid 1 的行?除了编写绝对完美的应用程序代码之外,什么都没有。

但是,如果您使用单表方法,并且必须更改 typeid 3 的父类别,则必须更改一百万个位置的数据以反映更改。这意味着在执行清理时锁定表,否则可能会同时插入新数据。

规范化有助于消除冗余存储信息,如果每个离散事实(例如 typeid 3 属于 categoryid 2)只存储一次,那么很容易原子地进行更改,并且自动更改对该行的所有引用的含义。

你是对的,需要更多的连接——但前提是你在整个地方都使用伪键,就像你正在做的那样。您不一定需要这样做,您可以改用自然键,并且对它们的引用将使用级联外键声明,因此查找表中的更改也会自动更新引用表。

当然,规范化规则并不要求使用伪键。这些规则对它们只字未提。


回复您的评论:键或代理键是用于标识行的“id”列。通常,这些值是通过自动递增机制分配的,该机制确保唯一性,同时允许并发事务插入行。id 的值对于它标识的行没有任何意义。


下面显示了您的表在正常形式下的样子,但没有代理键。

产品提供表

client | product        | customURL
===================================
Smith  | Rawlings Glove | 1111
Smith  | Product 2      | 2222
Smith  | Product 3      | 3333
Smith  | Product 4      | 4444
Jones  | Rawlings Glove | 5555
Jones  | Product 4      | 6666

产品定义表

product        | type
=======================
Rawlings Glove | Gloves
Product 2      | Bats
Product 3      | Bats
Product 4      | Shoes

类型定义表

type      | category
==============================
Gloves    | Baseball Equipment
Bats      | Baseball Equipment
Shoes     | Football Equipment
Footballs | Football Equipment

类别定义表

category
==================
Baseball Equipment
Football Equipment

使用非整数作为主键列的数据类型是完全符合关系数据库设计和规范化的,因此外键从其他表中引用它们。

为了性能或简洁性或允许其他列中的值自由更改,有充分的理由使用代理键。但是规范化并不要求使用代理键。

于 2012-11-19T22:43:11.937 回答
1

我会采用标准化方法,因为无论如何您都必须使用平面方法维护类别和类型名称(以及可能的其他属性)的单独查找表。

您可能会考虑使用以下表格将类别和类型更改为通用树结构:

 create table product_hierarchy(
    id integer primary key,
    name character,
    parent_id references product_hierarchy)

...因为它可以让客户灵活地增加层次结构的深度。

于 2012-11-19T22:38:16.450 回答
1

要尝试解决您的直接问题:

我是不是想太多了?

取决于您的应用程序有多大,以及您使用什么引擎来存储数据。由于您打算将其放入 MySQL 表中,因此您的想法非常合适。

两种方法不是以相同的方式获得最终解决方案吗(后者只涉及几个连接来收集平面表,如图 1 所示)?

嗯,是的,但是引用维基百科

数据库规范化是组织关系数据库的字段和表以最小化冗余和依赖性的过程。规范化通常涉及将大表划分为较小(且冗余较少)的表并定义它们之间的关系。目标是隔离数据,以便仅在一个表中对字段进行添加、删除和修改,然后通过定义的关系传播到数据库的其余部分。

将您的数据分解为您描述的结构(顺便说一句,我同意),将使您能够最轻松地维护您的数据。将类别和类型数据与“提供的产品”保持在同一个表中会产生大量冗余数据。当然,我真的无法想象您需要在哪里更新这些数据,但如果您这样做了,您将不得不更新很多记录。在您建议的结构中,要更新的记录数最少。

于 2012-11-19T22:39:29.367 回答
0

在第一种方法中,您忘记了每个类别、类型和产品 ID 的名称列。如果您添加此信息,它可以工作,但其他方法似乎已经工作。当您使用 4 个不同的表时,您有更多空间。

于 2012-11-19T22:31:13.873 回答