【问题标题】:SELECT de-normalized columns into separate records?将非规范化列选择为单独的记录?
【发布时间】:2012-05-02 06:40:36
【问题描述】:

我只是在玩弄 SQL,所以如果我在工作面试中被问到,我并不是完全不了解它。我的朋友最近在一次采访中被问到以下问题,他无法得到它,我问了一个工作中的人,他体面地了解 SQL,但他不知道。你们能帮我回答这个问题,然后解释它是如何工作的吗?请问?

*问题*

数据库规范化(或缺乏规范化)通常会给开发人员带来挑战。

考虑一个包含三个字段的员工数据库表:

EmployeeID
EmployeeName
EmailAddresses

每个员工都由唯一的 EmployeeID 标识,在 EmailAddresses 字段中可能有一个或多个以逗号分隔的 @rockauto.com 电子邮件地址。

数据库表定义如下:

CREATE TABLE Employees
(
  EmployeeID int UNSIGNED NOT NULL PRIMARY KEY,
  EmployeeName varchar(50) NOT NULL,
  EmailAddresses varchar(40) NOT NULL ,
  PRIMARY KEY(EmployeeID)
);

出于测试目的,以下是一些示例数据:

INSERT INTO Employees (EmployeeID, EmployeeName, EmailAddresses) VALUES
('1', 'Bill', 'bill@companyx.com'),
('2', 'Fred', 'fred@companyx.com,freddie@companyx.com'),
('3', 'Fred', 'fredsmith@companyx.com'),
('4', 'Joe', 'joe@companyx.com,joe_smith@companyx.com');

您的任务是编写一个 MySQL SELECT 查询,该查询将为上面的示例数据显示以下输出:

Employee    EmailAddress
Bill    bill@companyx.com
Fred (2)    fred@companyx.com
Fred (2)    freddie@companyx.com
Fred (3)    fredsmith@companyx.com
Joe     joe@companyx.com
Joe     joe_smith@companyx.com

请注意,由于有多个同名的人(在本例中为“Fred”),因此 EmployeeID 包含在括号中。

您的查询需要使用 MySQL 5.1.41 兼容的语法编写。您应该假设排序是使用标准数据库升序完成的:“ORDER BY EmployeeID ASC”

对于这个问题,您需要提交一个 SQL SELECT 查询。您的查询应该能够在合理的时间内处理包含 1000 条记录的表。

【问题讨论】:

  • 只是我的看法......这是一个糟糕的面试问题。
  • 我们是否允许使用 php 来解析 select 语句,或者查询是否必须直接以该格式提取
  • 一个更好的问题是如何规范这个数据库结构^^
  • 我正在努力解决的问题是取一行并将列值拆分为多行。没有直接的方法可以做到这一点。
  • 第 1 步,找出造成此问题的人并伤害他们。

标签: mysql sql


【解决方案1】:

仅当您的电子邮件少于 10000 封时.... 可以接受吗?

select 
       if(t1.c > 1, concat(e.employeename, ' (', e.employeeid, ')'), e.employeename) as Employee,
       replace(substring(substring_index(e.EmailAddresses, ',', n.row), length(substring_index(e.EmailAddresses, ',', n.row - 1)) + 1), ',', '') EmailAddress 
from 
       (select employeename, count(*) as c from Employees group by employeename) as t1, 
       (select EmployeeID, length(EmailAddresses) - length(replace(EmailAddresses,',','')) + 1 as emails from Employees) as t2,
       (SELECT @row := @row + 1 as row FROM 
       (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) x,
       (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) x2, 
       (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) x3, 
       (select 0 union all select 1 union all select 3 union all select 4 union all select 5 union all select 6 union all select 6 union all select 7 union all select 8 union all select 9) x4, 
       (SELECT @row:=0) as ff) as n,
       Employees e
where 
      e.employeename = t1.employeename and
      e.employeeid = t2.employeeid and
      n.row <= t2.emails
order by e.employeeid;

编辑:

生成的无用数字更少:

select 
       if(t1.c > 1, concat(e.EmployeeName, ' (', e.EmployeeID, ')'), e.EmployeeName) as Employee,
       replace(substring(substring_index(e.EmailAddresses, ',', n.row), length(substring_index(e.EmailAddresses, ',', n.row - 1)) + 1), ',', '') as EmailAddress 
from 
       (select EmployeeName, count(*) as c from Employees group by EmployeeName) as t1, 
       (select EmployeeID, length(EmailAddresses) - length(replace(EmailAddresses,',','')) + 1 as emails from Employees) as t2,
       (select `1` as row from (select 1 union all select 2 union all select 3 union all select 4) x) as n,
       Employees e
where 
      e.EmployeeName = t1.EmployeeName and
      e.EmployeeID = t2.EmployeeID and
      n.row <= t2.emails
order by e.EmployeeID;

我们学到了什么?糟糕的数据库设计会导致糟糕的查询。你可以用 SQL 做一些事情,这可能只是因为人们做了糟糕的数据库设计...... :)

【讨论】:

  • email 字段只有 40 个字符长,所以里面的事件不能容纳 10 个字符
  • 好点...数字生成可以大大简化。显然没有办法在 MySQL 中生成参数化的行数。
猜你喜欢
  • 2014-07-22
  • 2011-09-19
  • 2010-11-20
  • 1970-01-01
  • 2021-12-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-21
相关资源
最近更新 更多