数据库设计 - 关系与属性

时间:2014-10-15 18:06:12

标签: mysql sql database database-design relational-database

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

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

因此我们需要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列就可以了。

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

在我看来,为此创建关系表是没有意义的,因为使用列表(select)我们只会在用户实际粘贴到数据库时限制用户。从理论上讲,我们可以允许用户手动输入他喜欢的颜色(例如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个额外的数据透视表,我可以轻松地让用户选择最多10种颜色,而我根本不需要重新设计数据库。但也会出现不利因素 - 搜索困难,工作繁琐,很多人加入。

详细问题

总而言之 - 假设哪种解决方案更好:

  1. 我可能不会改变一个属性的最大数量(如果我决定允许最多3个爱好,这可能永远不会改变)
  2. 许多字段的选择列表相对较短(大多数字段小于10)
  3. 我需要在这样的数据库中搜索很多。例如,有人想要搜索fav_colour设置为红色且有爱好音乐的用户。
  4. 如果有任何其他解决方案或优点/缺点,您会看到我感谢与您分享。

4 个答案:

答案 0 :(得分:1)

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

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

我建议使用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。

答案 1 :(得分:1)

如果用户可以经常更改喜欢的颜色/爱好,我会使用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'

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

答案 2 :(得分:1)

除非确实需要,否则您希望避免额外的表和连接。这正是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');

答案 3 :(得分:0)

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

但对我来说,会使用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功能)