如何建模涉及产品、类别和标签的三向关联?

yem*_*met 2 mysql foreign-key database-design primary-key many-to-many

我有以下三个表:

products:
    product_id,
    product_name,
    ...

categories:
    category_id,
    category_name,
    category_parent_id,
    category_priority (for sort ordering),
    .....

labels:
    label_id,
    label_name,
    .....
Run Code Online (Sandbox Code Playgroud)

这个想法是,产品分配给一个类别将各内进行分组类别标签,并以这种方式在网站上列出:

---label1---
product_1
product_2
product_3
---label2---
product_4
product_5
---label3---
product_6
product_7
product_8
product_9
etc.
Run Code Online (Sandbox Code Playgroud)

我不知道如何设计一个关联表(或多个表)将所有这些粘合在一起并防止这样的异常:

---label1---
product_1
product_2
---label2---
product_2
product_3
Run Code Online (Sandbox Code Playgroud)

同时,我想允许一种情况,即当没有足够的产品来证明其合理性时,不会为某个类别分配标签

问题

是否有可能设计一个将它结合在一起的结构,或者我应该“放弃所有希望”并采用这样的方法:

categories:
    category_id,
    category_name,
    category_parent_id,
    categor_is_label,
    category_priority

products:
    product_id,
    product_name,
    ...
Run Code Online (Sandbox Code Playgroud)

加上下面的关联表:

categories_products:
    category_id,
    product_id,
    priority
Run Code Online (Sandbox Code Playgroud)

并处理应用程序中的所有逻辑和异常检查?

我假设用户不能直接访问数据库。

评论和聊天互动

对于那些有兴趣深入讨论手头业务背景的人,您可以访问此聊天室

MDC*_*CCL 5

注意:此答案提供了一种方法,专门涵盖通过最早的一系列评论和聊天交互(其中大部分可以在此问题修订版中看到)派生的业务规则。尽管如此,在进行了更深入的澄清和审议之后@yemet表示,由于对不同业务规则的连续识别,业务上下文可能需要某种不同的方法。


您已经确定存在涉及感兴趣的实体类型(一旦实现的表)的三向关联(也称为三元菱形关系)这一事实表明您正朝着正确的方向前进。

商业规则

目标应该是分别处理手头的三种不同关系,在全面考虑实施方面之前从逻辑级别分析开始。在这方面,写下一些描述相关业务规则的公式是很有帮助的,例如:

首先,对于以下多对多 (M:N) 关系:

  • A product is classified by one-to-many categories
  • A category classifies zero-one-or-many products

这意味着存在我将要调用的关联实体类型product_category

其次,对于不同的 M:N 关系:

  • A category is integrated by zero-one-or-many labels
  • A label integrates zero-one-or-many categories

表明存在另一种关联实体类型的情况,在这种情况下,我将命名为category_label

然后,是时候管理另一个 M:N 关系,这次是在上面讨论的两个关联实体类型之间:

  • A product_category may receive zero-one-or-many label_assignments
  • A category_label may take part in zero-one-or-many label_assignments

如前所述,我已经包含了一个我命名的新实体类型,label_assignment但是,自然地,您可以使用对您的业务领域更有意义的术语来命名它。

我已经假设,根据categories您的问题中包含的表的结构(特别是列categories.category_parent_id),存在关于名为 的实体类型的自递归一对多 (1:M) 关系category。后来你确认了这种情况,所以下面的规则也适用:

  • A category comprises zero-one-or-many categories

逻辑模型

然后我从上面介绍的业务规则公式中导出了一个 IDEF1X 1逻辑模型,如图 1所示:

图 1 - 产品分类数据模型

通过这种安排,您可以解决大部分需求,因为:

  • 每个产品在收到标签分配之前必须首先与某个类别相关联。
  • 标签不能被分配到特定的产品,如果它没有被连接到一定的类别先前。
  • 产物可以与某个类别,而不必涉及在具有一个关系标签

说明性 DDL 结构

因此,我编写了以下 DDL 结构(在SQL Fiddle上测试):

-- You should determine which are the most fitting 
-- data types and sizes for all your table columns 
-- depending on your business context characteristics.

-- As one would expect, you are free to make use of 
-- your preferred (or required) naming conventions.

CREATE TABLE product
(
     product_id       INT      NOT NULL,
     product_code     CHAR(30) NOT NULL,
     name             CHAR(30) NOT NULL,
     description      CHAR(90) NOT NULL,
     created_datetime DATETIME NOT NULL,
     CONSTRAINT PK_product             PRIMARY KEY (product_id),
     CONSTRAINT AK_product_code        UNIQUE      (product_code), -- (Possible?) ALTERNATE KEY.
     CONSTRAINT AK_product_name        UNIQUE      (name),         -- ALTERNATE KEY.
     CONSTRAINT AK_product_description UNIQUE      (description)   -- ALTERNATE KEY.
);

CREATE TABLE category
(
    category_number         INT      NOT NULL,
    parent_category_number  INT      NULL, -- Set up as ‘NULLable’, in order to focus on the main aspects of the approach exposed.
    name                    CHAR(30) NOT NULL,  
    description             CHAR(90) NOT NULL,
    created_datetime        DATETIME NOT NULL,
    CONSTRAINT PK_category                         PRIMARY KEY (category_number),
    CONSTRAINT AK_category_name                    UNIQUE      (name),        -- ALTERNATE KEY.  
    CONSTRAINT AK_category_description             UNIQUE      (description), -- ALTERNATE KEY.
    CONSTRAINT FK_FROM_category_TO_parent_category FOREIGN KEY (parent_category_number)
        REFERENCES category  (category_number)
);

CREATE TABLE label
(
    label_number     INT      NOT NULL,
    name             CHAR(30) NOT NULL,    
    description      CHAR(90) NOT NULL,  
    created_datetime DATETIME NOT NULL,
    CONSTRAINT PK_label             PRIMARY KEY (label_number),
    CONSTRAINT AK_label_name        UNIQUE      (name),       -- ALTERNATE KEY.
    CONSTRAINT AK_label_description UNIQUE      (description) -- ALTERNATE KEY.  
);

CREATE TABLE product_category -- Associative table.
(
    product_id          INT      NOT NULL,
    category_number     INT      NOT NULL, 
    classified_datetime DATETIME NOT NULL,  
    CONSTRAINT PK_product_category                  PRIMARY KEY (product_id, category_number),
    CONSTRAINT FK_FROM_product_category_TO_product  FOREIGN KEY (product_id)
        REFERENCES product  (product_id),
    CONSTRAINT FK_FROM_product_category_TO_category FOREIGN KEY (category_number)
        REFERENCES category (category_number)
);

CREATE TABLE category_label -- Associative table.
(
    category_number     INT      NOT NULL,
    label_number        INT      NOT NULL,   
    integrated_datetime DATETIME NOT NULL,  
    CONSTRAINT PK_category_label                  PRIMARY KEY (category_number, label_number),
    CONSTRAINT FK_FROM_category_label_TO_category FOREIGN KEY (category_number)
        REFERENCES category (category_number),
    CONSTRAINT FK_FROM_category_label_TO_label    FOREIGN KEY (label_number)
        REFERENCES label    (label_number)
);

CREATE TABLE label_assignment -- Associative table that ‘concretizes’ a relationship between two distinct relationships.
(
    product_id        INT       NOT NULL,
    category_number   INT       NOT NULL,
    label_number      INT       NOT NULL,  
    assigned_datetime DATETIME  NOT NULL,
    CONSTRAINT PK_label_assignment                          PRIMARY KEY (product_id, category_number, label_number), -- Composite PRIMARY KEY.
    CONSTRAINT FK_FROM_label_assignment_TO_product_category FOREIGN KEY (product_id, category_number)   -- Composite FOREIGN KEY.
        REFERENCES product_category (product_id, category_number),
    CONSTRAINT FK_FROM_label_assignment_TO_category_label   FOREIGN KEY (category_number, label_number) -- Composite FOREIGN KEY.
        REFERENCES category_label   (category_number, label_number)
 );
Run Code Online (Sandbox Code Playgroud)

特别注意表的两个复合 FOREIGN KEY 定义label_assignment,因为category_number属性包含在它们中。

您提出了一项要求,其中规定:

必须将产品分配到类别才能在商店中可见。

因此,您应该保证每次插入product一行时,您也可以通过关联表中一行的 INSERTion它与某个行链接起来,category这里称为product_category. 这样,两个操作都应该在同一个ACID TRANSACTION 中执行,以便它们作为一个单元要么成功要么失败。

类似场景

你可能会发现我的回答对你有帮助

还有@Ypercube??回答


尾注

1.对信息建模(综合定义IDEF1X)是被确立为一个非常可取的数据建模技术标准通过标准的美国国家技术研究所(NIST)于1993年12月。它坚实基础的(一)理论著作撰写由始发的的关系模型,即EF科德博士; 关于 (b)实体关系视图,由PP Chen 博士开发;以及 (c) 逻辑数据库设计技术,由 Robert G. Brown 创建。值得注意的是,IDEF1X 是通过一阶逻辑形式化的。