【问题标题】:Mysql NormalizationMysql 规范化
【发布时间】:2012-04-19 22:55:14
【问题描述】:

我有三张桌子

USER TABLE
userid  username
1       alpha

Email TABLE
id  userid email
1   1      alpha1@test.com
2   1      alpha2@test.com
3   1      alpha3@test.com

Phonenumber TABLE
id  userid  phonenumber
1   1       123456
2   1       123457
3   1       123458

如何使用单个查询获得以下结果

userid username email           phonenumber
1      alpha    alpha1@test.com 123456
1      alpha    alpha2@test.com 123457
1      alpha    alpha3@test.com 123458

【问题讨论】:

  • 我希望上面的规范化只是一个例子,否则每列的新表都没用:)
  • 我知道我可以通过加入来做到这一点,但我得到了 9 行。所以我只需要 3 行我的问题中提到的结果
  • 你怎么知道哪个邮箱属于哪个电话号码?
  • 如果有 4 个电子邮件地址和 2 个电话号码怎么办?预期的输出是什么?

标签: mysql normalization


【解决方案1】:

让我们从完全未标准化的数据版本开始,然后添加一些额外的、合理的数据,以便我们了解标准化的工作原理。 (假设每个人至少有一个电子邮件地址和一个电话号码,只是为了避免谈论空值。)

userid  username  email             phonenumber
1       Alpha     alpha1@test.com   123456
1       Alpha     alpha2@test.com   123457
1       Alpha     alpha3@test.com   123458
2       Beta      beta1@test.com    234567
2       Beta      beta2@test.com    234567      (2 email addresses, 1 phone)
3       Gamma     gamma1@test.com   234678
3       Gamma     gamma1@test.com   234679      (1 email address, 2 phones)
4       Alpha     alpha32@test.com  345678      (2 people, #1 and #4, with same name)

如果您仔细查看该数据,您会发现唯一的键是 {email, phonenumber}

这就是为什么您无法仅获取三行的原因——该键不在您的表中。这就是@ontrack 所说的“您的表格在电子邮件和电话号码之间没有唯一的关系”。

遵循任何数据库教科书中确定候选键的算法都会给你同样的结果。 AFAIK,每一本数据库理论教科书都有至少一种算法来确定候选键。

显然,如果您有一个以 {email, phonenumber} 作为键的表,则用户 ID 1 只会显示 3 行。

【讨论】:

  • 感谢您的回答,但我别无选择,因为我无法在生产数据库中进行任何更改。我只需要玩 mysql 查询。如果无法使用 mysql 获取数据,那么我肯定必须在脚本级别处理这个问题。
  • @neeraj:您无法在脚本级别处理它,因为您需要的事实不在数据库中。如果 Phonenumber.id 和 Email.id 是自动增量整数(我们的一个合理假设),则没有确定性的方法来判断“alpha1@test.com”应该与“123456”而不是“123458”配对。
  • 你能看看我在dba.stackexchange.com/questions/16173/… 的另一个查询吗?如果该查询有解决方案,那么我想我可以找到这个问题的解决方案。
【解决方案2】:

我无法回答您的问题,但您是否考虑过使用group_concat()

SELECT userid, username, GROUP_CONCAT(DISTINCT email), GROUP_CONCAT(DISTINCT phonenumber) 
FROM Email 
LEFT JOIN Phonenumber USING (userid) 
LEFT JOIN User USING (userid)
GROUP BY userid

它应该给你这个结果:

userid username  email                                           phonenumber
1      alpha     alpha1@test.com,alpha2@test.com,alpha3@test.com 123456,123457,123458

也许这会解决你原来的问题?

【讨论】:

  • 感谢您的回复。我没有使用 group_concat() 因为它以单个字符串返回结果。
【解决方案3】:

很简单。 joining 您的表格。

试试下面:

SELECT u.userid,u.username,e.email,p.phonenumber 
FROM User as u LEFT JOIN Email as e on u.userid=e.userid
LEFT JOIN Phonenumber as p on u.userid=p.userid

【讨论】:

  • 我知道我可以通过加入来做到这一点,但我得到了 9 行。所以我只需要 3 行我的问题中提到的结果
  • 感谢您的回复,但这不是一个正确的查询,因为我仍然得到 9 行。您首先尝试了解我如何需要结果然后编写查询并运行的问题
  • 您的表格在电子邮件和电话号码之间没有唯一的关系。如果这些电子邮件地址在每一行中都不同,那么查询应该如何知道某个电话号码与之相关?因此,可以使用 DISTINCT 从该查询中获取 3 行。但是您应该首先了解并解释您要检索的是什么以及为什么它应该导致 3 行。
  • 根据user表的userid,user、email、phonenumber之间存在关系。现在我知道 distinct 会给我唯一的行,但我想从每个表(电子邮件和电话号码)中获取行作为一列。这是非常普遍的场景,其中使用不同的表来保存基表的多个值。你是想说这个归一化不行吗??
  • 将所有内容连接在一起将为您提供 9 行,因为电子邮件地址的第一行与电话号码的第一行之间没有关系。为什么第一个电子邮件地址与第一个电话号码相关。您必须在模型中指定它,否则它确实没有正确规范化。
【解决方案4】:

从电子邮件左连接中选择用户 ID、用户名、电子邮件、电话号码 Phonenumber USING (userid) LEFT JOIN User USING (userid)

【讨论】:

  • 这是 9 行。我只需要 3 行
猜你喜欢
  • 2013-08-21
  • 2018-06-06
  • 2012-03-08
  • 2011-12-16
  • 2013-07-17
  • 2012-01-15
  • 1970-01-01
  • 2012-05-22
相关资源
最近更新 更多