处理m2m字段时的关系数据库设计

时间:2018-12-06 19:36:58

标签: sql database-design

假设我有以下表格:

User : id, login, password

Card : id, name, value

一个User可以多次具有相同的Card。例如:ID为1的User可以有ID为1的多个Card

我将创建下表:

UserCard : id, user_id, card_id

因此对于该示例,UserCard可能类似于:

| id | user_id | card_id |
|1   | 1       | 1       |
|2   | 1       | 1       |
|3   | 1       | 2       |

但是我与之讨论过的一位同事告诉我,有些行具有相同的值是不好的设计,并且

| id | user_id | card_id | quantity |
|1   | 1       | 1       | 2        |
|2   | 1       | 2       | 1        |

这将是最好的方法,因为它会删除重复的行。

我认为这是错误的,原因如下:

  • quantity可以计算,因此该字段无用
  • 添加quantity字段会添加很多不必要的内容,例如在向用户添加卡之前,我必须检查用户是否已经拥有此卡(如果他已经拥有了,我会增加数量字段,否则我会添加卡)
  • 我应该让DBMS处理这类事情

我的同事的观点是:

  • 在仅用于其他两个表之间关系的表中重复行是肮脏的
  • 我不记得他提出的其他观点,但是对我来说,我的建议并没有得到最优化。

问题:

在这种情况下最佳的数据库设计是什么? 一个用户可以拥有0张或任何具有相同ID的卡

编辑:

我们在PostgreSQL中使用Django 没有同事,我只是想知道什么是最好的数据库设计

2 个答案:

答案 0 :(得分:1)

您的同事是对的。我会给您介绍一下该理论,并举例说明原因,但是阅读CJ Date的教科书会更好。

UserCard表的基本问题是它没有自然键。您在其上贴上了无意义的id以使其“唯一”,但id 1和id 2之间没有有意义的区别:它们的值没有差异,因此它们代表的东西是难以区分。当您发现自己无法分辨的事物时,通常的解决方案是对它们进行计数。那就是您用美元做的银行业务。

关系理论基于一组不同的(非重复)元素。您无视这种危险的基础。

实际上(仅举一个例子),当用户丢一张卡时会发生什么?您将如何删除无法区分的行?你的同事可能会说

update UserCard set quantity = quantity - 1 where user = 1 and card = 1
delete UserCard where quantity = 0

你会怎么做?使用另一种limit 1这样的非理论难题来解决问题?如果限制不为1,因为任意一组用户丢了卡,该怎么办?

您部分地通过人为的独特性来帮助自己:

delete UserCard where id = 
       (select min(id) from UserCard where user = 1 and card = 1)

说明了唯一性是如何帮助的。但是quantity是您的朋友。因此,如果您使用该理论而不是与之抗争,那么您的设计和查询都将更加简单。

答案 1 :(得分:0)

希望这可以帮助您

create table #user
(
    id int identity(1,1),
    login nvarchar(100),
    password nvarchar(100)
)
create table #card
(
    id int identity(1,1),
    name nvarchar(100),
    value int
)
create table #usercard
(
    id int identity(1,1),
    user_id int, /* <-- (in theory) foreign key to #user table*/
    card_id int /* <-- (in theory) foreign key to #card table */
)
insert into #user (login, password) values
('user1','password1'),
('user2','password2')

insert into #card (name, value) values
('card1', 1),
('card2', 2),
('card3', 3)

insert into #usercard (user_id, card_id) values
(1,1),
(1,2),
(2,3)

select a.id as user_id, count(b.card_id) as quantity from #user a
inner join #usercard b on a.id = b.user_id
inner join #card c on b.card_id = c.id
group by a.id

在数据库设计中,第三个表(#usercard)称为“连接表”。当您希望实现数据库规范化时,首选此设计。