繁体   English   中英

数据库设计 - 关系与属性

[英]Database design - relations vs properties

我在设计数据库(SQL / MySQL)时遇到问题。 假设我们有一个用户,用户可以有很多朋友和很多帖子,并填写一些关于他自己的数据

很明显,对于friends我们需要一个pivot_table用于n:n关系,对于posts我们需要创建一个带有user_id(1:n)关系的额外表。

所以我们需要usersuser_friendsposts表。 这很明显。 这就是应该如何处理关系。

但现在让我们假设我们希望用户拥有以下数据:

name - text
description - text
marital status - select only one from list
favourite colour - select only one from list
hobby - select up to 3 from list

对于文本字段(名称,描述),我们只需在users表中创建varchar / text列就可以了。

一般问题是 :如何处理其他字段(从列表中选择)? 我应该为它们创建关系,还是应该用它们创建标准数据列?

在我看来,没有必要为此创建关系表,因为使用列表(选择)我们只限制用户实际上可以粘贴到数据库中。 从理论上讲,我们可以允许用户手动输入他喜欢的颜色(例如red ,如果他输入错误的东西,例如reds我们会比较它将允许的colours列表)。 性别也是如此 - 我认为,当我们只拥有女性和男性并为其创造关系时,创造额外的表格是没有意义的。

第一个DB设计:

例如,我可以为属性创建以下列:

marital_status - int
fav_colour - int
hobby_1 - int
hobby_2 - int
hobby_3 - int

还有另外一个表(甚至用PHP或其他语言的普通数组),我存储值为1的fav_colour例如是红色,值为2的爱好是音乐等等(我在这里存储这些值无关紧要) - 我也可以使用enum类型)。

对我来说,这种态度的好处并不是创造许多实际上是属性而不是关系的关系(如上所述),所以工作量少,更容易获得有关用户的信息 - 你不需要使用任何重要的联接如果你有用户例如20或100这样的属性,我可以很容易地在用户表中搜索。 缺点也很明显 - 数据没有标准化,对于任何多选(例如爱好)我需要创建3列,如果将来我决定用户可以选择不是1颜色而是2或3,我需要添加2个额外的列。

替代数据库设计:

我创建额外的表: colourshobbiesmarital_statuses和我创建3个枢轴表: user_coloursuser_hobbiesuser_marital_statuses 缺点:很多加入。 优点 - 如果我创建3个额外的数据透视表,II可以轻松地允许用户选择多达10种颜色,我根本不需要重新设计数据库。 但是也会出现缺点 - 搜索困难,工作繁琐,很多人加入。

详细的问题

总结一下 - 假设哪种解决方案更好:

  1. 我可能不会改变一个属性的最大数量(如果我决定允许最多3个爱好,这可能不会改变)
  2. 许多领域的选择列表相对较短(大多数领域少于10)
  3. 我需要在这样的数据库中搜索很多东西。 例如,有人想搜索fav_colour设置为红色且具有爱好音乐的用户。

如果有任何其他解决方案或优点/缺点,您会看到我很高兴与我分享。

听起来您想对某些用户属性强制执行某些约束。 例如,喜欢的颜色必须是红色,绿色,蓝色,粉红色,橙色等; 婚姻状况必须是单身,离婚,结婚。

您已经描述了一种方法:查找表。 如果可能的值是动态的并且需要持续维护,或者存在许多可能的值,则这是最佳方法。 根据您的描述,这不是您的情况。 您可能的值将是非常静态和短暂的。

我建议使用sql CHECK约束。 有了它,您可以控制字段的可能值。 例如:

CREATE TABLE users
(
Name varchar(255) NOT NULL,
Description varchar(255),
Marital_Status varchar(10) NOT NULL,
Color varchar(10) NOT NULL,
CONSTRAINT chk_Color CHECK (Color in ('Red', 'Blue', 'Green', 'Orange')),
CONSTRAINT chk_Marriage CHECK (Marital_Status in ('Single', 'Married', 'Divorced'))
)

我没有语法检查此DDL语句,因此它可能包含标点符号错误。 此外,语法可能因您的特定DBMS而异。 我认为这应该适用于MySQL。

如果用户可以经常更改喜欢的颜色/爱好,我会使用lookup表,在我的示例中,我将它们称为decode表。 user/hobbiesuser/colors之间的所有关系都将在该decode表中找到。

由于你只能拥有1个marital status ,这很容易处理,这是1对多的关系。

创建一个包含2个字段的表Marital_StatusId (pk)Status(varchar(n))将不需要decode表来查找marital status

现在我建议创建一个表来容纳colors和一个hobbies表。 同样,我们做了marital status

Hobbies

HobbyId, Hobby

Colors
ColorId, Color

每当您需要添加/删除新的hobby/color请在这些decode表中执行此操作。

是否要为每个关系使用1个decode表或多个,即由您决定。 Hobby_Decode and Color_Decode

我将解释使用1的场景。

使用以下字段创建解码表...

Decode

Item_Type varchar(n)我们将在此字段中推送HobbyColor

UserId int - 自解释,保持用户的Id为“查找”

LookupId将持有HobbyColor id

让我创建一些示例数据,我们将继续这样做。

Hobbies table数据

 | HobbyId | Hobby

      1      Studying 
      2      Doing Drugs
      3      Drinking     

Colors table数据

 | ColorId | Color

     1        Red 
     2        Blue

我们在这里,这是我们的用户表。

Users

 | UserId | Name

      1     Marcin 
      2     CSharper

我喜欢喝酒,吸毒和红色,你是个书呆子所以你喜欢学习和蓝色。 在我们的解码表中,我们将添加以下条目来表示它。

Decode

 | Item_Type| UserId | LookUpId

    'Hobby'      2        2
    'Hobby'      2        3
    'Color'      2        1
    'Hobby'      1        1
    'Color'      1        2      

看那个解码表并没有真正告诉我们什么。 一旦我们将decode表加入到colors/hobbies ,就会很明显。

如果你想查看我的所有爱好和我最喜欢的颜色,查询将会看起来如此

注意:这是SQL Server语法而不是mysql。

--Pull Hobbies
Select u.Name, dH.Item_Type as 'Favorite', h.Hobby as 'Item'
from User u
inner join decode dH on dH.UserId = u.UserId 
                     and dH.Item_Type = 'Hobby'
inner join Hobby h on h.HobbyId = dH.LookUpId
where u.UserId = 2 

--Union in Colors
Union

Select u.Name, dH.Item_Type as 'Favorite', h.Hobby 'Item'
from User u
inner join decode dC on dH.UserId = u.UserId 
                     and dH.Item_Type = 'Color'
inner join Color c on c.ColorId = dH.LookUpId
where u.UserId = 2 

你的输出看起来像

|    Name    |    Favorite   |     Item 

   CSharper         Hobby         Drinking
   CSharper         Hobby         Doing Drugs
   CSharper         Color         Red

如果它是这样设置的,那么很容易更改/更新人们最喜欢的爱好和颜色。 decode表将处理所有这些。 它只需要简单地输入或删除该表。 而且这样,用户可以拥有无​​限的喜爱的爱好和颜色,因为它是驱动它的解码表,而不是用户表定义。

如果我们想要找到所有喜欢蓝色并且喝了查询的用户,那么请稍微操纵您的示例查询。

Select u.Name
from User u 
inner join decode d on d.UserId = u.UserId
inner join Hobby h on h.HobbyId = d.LookUpId and d.Item_Type = 'Hobby'
inner join Color c on C.ColorId = d.LookUpId and d.Item_Type = 'Color'
where h.Hobby = 'drinking' and c.Color = 'blue'

做这样的连接是完全可以接受的。

除非确实需要,否则您希望避免额外的表和连接。 这正是enums的用途。 枚举内部存储为整数,在使用中看起来像具有约束值的字符串。

create table users (
  user_id bigint unsigned not null auto_increment primary key,
  name varchar(255) not null,
  description varchar(255),
  marital_status enum('single', 'married'),
  favorite_color enum('red', 'green', 'blue'),
  hobby1 enum('painter', 'doctor', 'lawyer'),
  hobby2 enum('painter', 'doctor', 'lawyer'),
  hobby3 enum('painter', 'doctor', 'lawyer')
);

要插入一个值: insert into table users (name, marital_status) values ('Jack', 'single');

此语句将失败: insert into table users (name, marital_status) values ('Jack', 'abcd');

修改列表是一个简单而快速的操作: alter table users modify marital_status enum('divorced', 'single', 'married');

无论你选择哪一个都是好的,不要太依赖规范化。

但对我来说,将使用5个表usersmarital_statuscolourshobbiesuser_hobbies

CREATE TABLE users (
  user_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  description VARCHAR(255),
  marital_status INT,
  fav_colour INT
)

CREATE TABLE marital_status (
  id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL
)

CREATE TABLE colours (
  id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL,
  code VARCHAR(7)
)

CREATE TABLE hobbies (
  id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(255) NOT NULL
)

CREATE TABLE user_hobbies (
  id INT NOT NULL AUTO_INCREMENT PRIMARY KEY,
  user_id BIGINT,
  hobby_id INT
)

对于数据透视表,我建议从应用程序中单独创建/填充它们,例如使用命令行或消息队列(使用crontab功能)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM