【问题标题】:MySQL schema design issues - NormalizingMySQL 模式设计问题 - 规范化
【发布时间】:2013-11-14 07:51:34
【问题描述】:

我正在使用以下设计为我的网站创建表格

设计 1

设计 2

由于不是每个注册的用户都会尝试挑战,设计 1 很适合。在插入第三个表时,表 2 的分数会相应更新。但是 user_id 字段变得多余了。

设计 2 中的每个用户都设置了 0 或 NULL 值,但仍未标准化。 什么是最佳设计?规范化或关键在组织中有多重要?

【问题讨论】:

  • 图片中的每一行代表不同的表格吗?他们有名字吗?您能否进一步解释一下挑战是什么以及它们在图片中的表现方式?
  • 所以...您需要user 表吗?然后是challenge 表...?并且用户可以接受多个挑战,不同的用户可以完成挑战?你想记录分数吗?那是对的吗?你能解释一下发生了什么吗?
  • 是的,你是对的。他们确实有表名。用户,挑战,相应得分。它基本上就像stackoverflow的声誉。由于它显示在每个页面上,我需要存储用户在不同挑战中获得的总分。但是用户可能不会接受挑战,分数仍然为零并且仍然显示
  • 好吧....看看我在下面粘贴的答案,告诉我这是否适合你。我仍然不明白你在说什么......或者为什么“因为它显示在每个页面上,我需要存储用户在不同挑战中获得的总分。但用户可能不会接受挑战和分数保持为零并且仍然显示”与架构有关。

标签: mysql database database-design schema rdbms


【解决方案1】:

编辑

对于未来的人 - 我在理解 OP 的要求时遇到了一些问题,所以如果你迷路了,请通读 cmets。最终,他们希望存储聚合数据,但不知道将其放在哪里或如何实现。解决方案基本上是使用 insert trigger,在本文末尾附近对此进行了解释。

我选择在user 表中添加另一列来存储user_problem.score 的累积总和。然而,创建一个新表(包含user_idtotal_sum 列)并不是一个糟糕的选择,即使它似乎过度使用了规范化。有时最好将不断更新的数据与很少更改的数据分开。这样一来,如果出现问题,您就知道您的静态数据是安全的。

我从未涉及过的其他问题是与存储聚合数据相关的数据并发完整性问题...所以要小心。


我会建议这样的事情:

User Table
User_ID  -  Email  -  Name  -  Password  -  FB_ID 
-- holds all the user information 


Problem Table
Problem_ID  -  Problem_Title  -  Problem_Descr 
-- holds all the info on the individual challenges/problems/whatever


User_Problem Table
User_Problem_ID  -  User_ID  -  Problem_ID  -  Score  -  Completion_Date
-- Joins the User and Problem tables and has information specific
-- to a user+challenge pair 

这假设用户可以接受许多挑战/问题。一个问题/挑战可以由多个用户承担。

要查看某个用户的所有问题,您可以执行以下操作:

select  user.user_id, 
        user.name,
        problem_title, 
        problem_descr, 
        user_problem.score, 
        user_problem.completed_date

from    user 

        join user_problem on user.user_id = user_problem.user_id 

        join problem on user_problem.problem_id = problem.problem_id 

where   user.user_id = 123 or user.email = 'stuff@gmail.com'

varchar 字段的长度相当通用...

create table User(
  User_ID   int unsigned auto_increment primary key,
  Email     varchar(100), 
  Name      varchar(100), 
  Password  varchar(100), 
  FB_ID     int
); 

create table Problem (
  Problem_ID    int unsigned auto_increment primary key,
  Problem_Title varchar(100), 
  Problem_Descr varchar(500)
); 

create table User_Problem (
  User_Problem_ID int unsigned auto_increment primary key, 
  User_ID         int unsigned,
  Problem_ID      int unsigned, 
  Score           int,
  Completion_Date datetime,

  foreign key (User_ID) references User (User_ID), 
  foreign key (Problem_ID) references Problem (Problem_ID)
); 

在我们从下方在 cmets 中进行对话之后……您将向用户添加一列:

User Table
User_ID  -  Email  -  Name  -  Password  -  FB_ID  -  Total_Score

我为该列指定了默认值 0,因为如果此人没有任何相关的问题/挑战,您似乎想要/需要该值。根据其他情况,如果您有一条规定永远不会有负分的规则,那么将其设为unsigned int 可能会对您有所帮助。

alter table user add column Total_Score int default 0;

那么...您将在user_problem 表上使用影响user 表的插入触发器。

CREATE TRIGGER tgr_update_total_score 

AFTER INSERT ON User_Problem 
FOR EACH ROW

  UPDATE User
     SET Total_score = Total_score + New.Score
   WHERE User_ID = NEW.User_ID;

所以...在将一行添加到User_Problem 后,您会将新分数添加到user.total_score...

mysql> select * from user;
+---------+-------+------+----------+-------+-------------+
| User_ID | Email | Name | Password | FB_ID | Total_Score |
+---------+-------+------+----------+-------+-------------+
|       1 | NULL  | kim  | NULL     |  NULL |           0 |
|       2 | NULL  | kyle | NULL     |  NULL |           0 |
+---------+-------+------+----------+-------+-------------+
2 rows in set (0.00 sec)

mysql> insert into user_problem values (null,1,1,10,now());
Query OK, 1 row affected (0.16 sec)

mysql> select * from user;
+---------+-------+------+----------+-------+-------------+
| User_ID | Email | Name | Password | FB_ID | Total_Score |
+---------+-------+------+----------+-------+-------------+
|       1 | NULL  | kim  | NULL     |  NULL |          10 |
|       2 | NULL  | kyle | NULL     |  NULL |           0 |
+---------+-------+------+----------+-------+-------------+
2 rows in set (0.00 sec)

mysql> select * from user_problem;
+-----------------+---------+------------+-------+---------------------+
| User_Problem_ID | User_ID | Problem_ID | Score | Completion_Date     |
+-----------------+---------+------------+-------+---------------------+
|               1 |       1 |          1 |    10 | 2013-11-03 11:31:53 |
+-----------------+---------+------------+-------+---------------------+
1 row in set (0.00 sec)

【讨论】:

  • 但是根据上面的设计,假设一个用户已经接受了很多挑战,那么每次都要计算总分
  • 好吧...我不确定我是否在关注你。您可以有一个存储计算的分数值的表(或用户中的字段)......?并使用插入触发器,因此每当将一行添加到 user_problem 时,它都会更新总分(无论您决定将其放置在哪里)。
  • 好吧,我的意思是假设用户 A 完成了 10 个挑战,并为每个挑战分配了 10 个学分。所以他的总分是 100。但是 100 不是直接存储的。每次我们查询分数时,都会汇总并显示为每个挑战获得的积分
  • 对...所以您只想将汇总的总数/数据存储在某个地方以便更快地访问?这就是你要找的东西吗?
  • 好的,非常感谢您的广泛帮助。我正在尝试实现最优化的设计。这行得通!
猜你喜欢
  • 2010-11-25
  • 1970-01-01
  • 1970-01-01
  • 2012-07-30
  • 2012-02-25
  • 1970-01-01
  • 2013-01-18
  • 2016-12-14
  • 2013-08-21
相关资源
最近更新 更多