【问题标题】:Relational database design issue - category scoped tags关系数据库设计问题 - 类别范围标签
【发布时间】:2017-08-10 11:27:38
【问题描述】:

我正在设计一个数据库,其中有许多产品,每个产品都属于一个且仅属于一个类别。

产品应该被标记,但只能使用它们所属类别允许的标记。

这是我目前得到的:

假设我有这 2 个类别:

  • 智能手机
  • 笔记本电脑

“智能手机”类别的标签:

  • 双卡
  • 全球定位系统

“笔记本电脑”类别的标签:

  • 背光键盘
  • HDMI

这种设计的问题是数据库不会阻止产品被标记为另一个类别的标签:我的应用程序代码中的一个错误很容易导致笔记本电脑被标记为“双 SIM 卡”标签,这显然不是我想要的。

我想在数据库级别使用外键而不使用触发器来防止这种情况。这可能吗?

【问题讨论】:

  • 为什么不在您的产品中加入二级密钥?所以双 SIM 卡有产品 id X 并且必须与类别 id Y 等一起使用?这样您就无法将产品重复用于不同的类别 - 但它可以解决您的需求。
  • 对不起,我没听懂。您是否建议在 products 表中添加一个新字段?
  • 是的。这样您的产品/标签属于特定类别 - 然后在您将新产品/标签添加到类别时维护这一点
  • products 和 tags 表中已经有一个“category_id”字段。所以每个产品和每个标签都已经属于一个类别。
  • 抱歉,不在您的 product_tag 表中,而是在您的 tag 表中。每个标签都应该指定它可以属于哪个类别。你永远无法让它“猜测” FK 应该如何,所以你需要指定哪些 category_ids 和哪些 product_ids 允许匹配。

标签: sql database database-design relational-database


【解决方案1】:

我能够在 Oracle 中执行以下操作。请注意最后一次插入是如何失败的,我相信这就是您所追求的。

CREATE TABLE product (
    id            INTEGER NOT NULL,
    category_id   INTEGER NOT NULL,
    PRIMARY KEY ( id ),
    CONSTRAINT uq_prod_cat UNIQUE ( id,category_id )
);

INSERT INTO product (
    id,
    category_id
) VALUES (
    1,
    1
);

CREATE TABLE tags (
    id            INTEGER NOT NULL,
    category_id   INTEGER NOT NULL,
    PRIMARY KEY ( id ),
    CONSTRAINT uq_tag_cat UNIQUE ( id,category_id )
);

INSERT INTO tags (
    id,
    category_id
) VALUES (
    1,
    1
);

INSERT INTO tags (
    id,
    category_id
) VALUES (
    2,
    1
);

INSERT INTO tags (
    id,
    category_id
) VALUES (
    3,
    2
);

CREATE TABLE product_tags (
    id            INTEGER NOT NULL,
    product_id    INTEGER NOT NULL,
    category_id   INTEGER NOT NULL,
    tag_id        INTEGER NOT NULL,
    PRIMARY KEY ( id ),
    FOREIGN KEY ( product_id,category_id )
        REFERENCES product ( id,category_id ),
    FOREIGN KEY ( tag_id,category_id )
        REFERENCES tags ( id,category_id )
);

INSERT INTO product_tags (
    id,
    product_id,
    category_id,
    tag_id
) VALUES (
    1,
    1,
    1,
    1
);

1 row inserted.

INSERT INTO product_tags (
    id,
    product_id,
    category_id,
    tag_id
) VALUES (
    2,
    1,
    1,
    2
);

1 row inserted.

INSERT INTO product_tags (
    id,
    product_id,
    category_id,
    tag_id
) VALUES (
    3,
    1,
    1,
    3
);

Error starting at line : 35 in command -
INSERT INTO product_tags (id, product_id, category_id, tag_id) VALUES (3, 1, 1, 3)
Error report -
ORA-02291: integrity constraint (SYS_C008023) violated - parent key not found

【讨论】:

  • 非常好的答案。我想知道您是否可以推荐一本我可以进一步研究的书?
  • 对于数据库,我推荐的唯一一本书是 Henry F. Korth 的“数据库系统概念”。但这是一本解释一般数据库概念的书,而不是关于特定 DBMS 的实用知识。它也很大。
【解决方案2】:

我的答案与Ashuntosh A 的答案非常相似,只是我会创建一个单独的表来将标签与类别相关联,以便架构允许标签应用于多个类别(例如平板电脑和手机)可能有一个 DualSim):

--TSQL

create table ProductCategory
(
    id int primary key identity,
    name varchar(50) not null
)

create table ProductTag
(
    id int primary key identity,
    name varchar(50) not null
)

create table TagCategory
(
    tag_id int foreign key references ProductTag,
    category int foreign key references ProductCategory,
    primary key (tag_id, category)
)

create table Product
(
    id int primary key identity,
    type int foreign key references ProductCategory,
    unique (id, type)
)

create table TaggedProduct
(
    product int,
    tag int,
    type int,
    primary key (product, tag),
    foreign key (product, type) references Product (id, type),
    foreign key (tag, type) references TagCategory (tag_id, category)
)



insert ProductCategory
    select 'Laptop' union
    select 'Phone'

insert ProductTag
    select 'HDMI' union
    select 'Backlit Keyboard' union
    select 'Dual Sim' union
    select 'GPS'

insert TagCategory
    select 1, 1 union -- HDMI/LAPTOP
    select 2, 1 union -- Backlit/LAPTOP
    select 3, 2 union -- DualSim/PHONE
    select 4, 2 -- GPS/PHONE    

insert Product
    select 1 --a laptop

insert TaggedProduct
    select 1, 1, 1 --laptop has hdmi
    union select 1, 2, 1 --laptop has backlit keyboard

insert TaggedProduct select 1, 3, 1 
--fails because 'DualSim/Laptop' is not a valid category

【讨论】:

  • 也不错,但在我的具体情况下,我不需要在类别之间共享标签
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-09-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多