我在设计数据库(SQL / MySQL)时遇到问题。假设我们有一个用户,用户可以有很多朋友和很多帖子,并填写一些关于他自己的数据。
很明显,对于friends
我们需要一个pivot_table用于n:n关系,对于posts
,我们需要创建一个带有user_id(1:n)关系的额外表。 / p>
因此我们需要users
,user_friends
和posts
表。这很明显。这就是应该如何处理关系。
但现在让我们假设我们希望用户拥有以下数据:
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
列表进行比较。同样适用于性别问题 - 我认为,当我们只持有女性和男性并为其创造关系时,创造额外的表格是没有意义的。
我可以为属性创建以下列:
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个额外的列。
我创建了额外的表:colours
,hobbies
,marital_statuses
,我创建了3个枢轴表:user_colours
,user_hobbies
,user_marital_statuses
。缺点:很多加入。优点 - 如果我创建了3个额外的数据透视表,我可以轻松地让用户选择最多10种颜色,而我根本不需要重新设计数据库。但也会出现不利因素 - 搜索困难,工作繁琐,很多人加入。
总而言之 - 假设哪种解决方案更好:
如果有任何其他解决方案或优点/缺点,您会看到我感谢与您分享。
答案 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/hobbies
和user/colors
之间的所有关系都可以在decode
表中找到。
由于您只能拥有1 marital status
,因此很容易处理它的1对多关系。
创建一个包含2个字段的表Marital_Status
,Id (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)
- 我们将在此字段中推送Hobby
或Color
UserId
int
- 自我解释,保持用户的ID为"查找"
LookupId
- 将保留Hobby
或Color
让我创建一些示例数据,我们将解决这个问题。
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个表users
,marital_status
,colours
,hobbies
,user_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功能)