【问题标题】:SQL - re-arrange a table via querySQL - 通过查询重新排列表
【发布时间】:2010-05-04 22:49:27
【问题描述】:

我继承了一张设计不佳的桌子。 它看起来像:

用户字段值 ------------------ 1 名亚伦 1 封电子邮件 aaron@company.com 1 部电话 800-555-4545 2 名字迈克 2 电子邮件 mike@group.org 2 电话 777-123-4567 (等等等等)

我希望通过查询以更合理的格式提取这些数据:

用户名 邮箱 电话 ------------------------------------------ 1 亚伦 aaron@company.com 800-555-4545 2 迈克 mike@group.org 777-123-4567

我是 SQL 新手,但尝试了几个使用 Group By 变体的查询, 一切都没有成功。

有没有一种 SQL 技术可以让这变得简单?

【问题讨论】:

  • 最好的方法取决于你使用的数据库系统,所以你应该提一下。
  • 这是一个 MySQL 数据库,通过 phpMyAdmin 访问。我希望能够使用简单、通用的 SQL 语法来做到这一点。

标签: sql


【解决方案1】:

这不是一张“设计糟糕的桌子”;但实际上是一个实体属性值 (EAV) 表。不幸的是,关系数据库是实现此类表的糟糕平台,并且否定了 RDBMS 的大部分优点。用错铲子钉螺丝的常见案例。

但我认为这可行(基于Marcus Adams' 的答案,我认为这行不通(编辑:现在可行))

SELECT User1.Value AS name, User2.Value AS email, User3.Value AS phone
FROM Users User1
LEFT JOIN Users User2
  ON User2.User = User1.User AND User2.Field='email'
LEFT JOIN Users User3
  ON User3.User = User1.User AND User3.Field='phone'
WHERE User1.Field = 'name'
ORDER BY User1.User

编辑:从其他答案(左连接和 ON 子句上的字段名称)中得到了一些细节,现在有人知道如何将剩余的 WHERE 放高一点吗? (但不是在第一次 JOIN 的 ON 时,那太丑陋了),当然没关系,因为查询优化器无论如何都会丑化它。

【讨论】:

  • @Javier,谢谢,我实际上发现了我的错误并修复了它。我不知道我在第一次编辑时在想什么。实体属性值模型可能是最常用的术语。
  • @Marcus:EAV 表!这就是我要找的词,谢谢! @Dolph:这是一个可爱的形象,但我不能相信它。我第一次看到它是由 Lua 语言的主要作者 Roberto Ierusalimschy 写的。
  • ...我又弄错了。引用来自 Django 的主要作者之一 Russell Keith-Magee。
【解决方案2】:

在我的工作中,我们很遗憾拥有这样的数据库设计。但是这种设计比传统的数据库设计更适合我们,因为我们必须存储不同的记录,并为我们提供所需的灵活性。我们使用的数据库存储了数百万条记录。

这将是使用 MSSQL 在大型数据库上运行查询的最快方法。它可以避免进行尽可能多的连接,这可能会非常昂贵。

DECLARE @Results TABLE
(
    UserID INT
    , Name VARCHAR(50)
    , Email VARCHAR(50)
    , Phone VARCHAR(50)
)

INSERT INTO @Results
    SELECT DISTINCT User FROM UserValues

UPDATE
    R
SET
    R.Name = UV.Value
FROM
    @Results R
INNER JOIN
    UserValues UV
    ON UV.User = R.UserID
WHERE
    UV.Field = 'name'

UPDATE
    R
SET
    R.Email = UV.Value
FROM
    @Results R
INNER JOIN
    UserValues UV
    ON UV.User = R.UserID
WHERE
    UV.Field = 'Email'

UPDATE
    R
SET
    R.Phone = UV.Value
FROM
    @Results R
INNER JOIN
    UserValues UV
    ON UV.User = R.UserID
WHERE
    UV.Field = 'Phone'


SELECT * FROM @Results

【讨论】:

  • 这可能是比较繁琐的方法,但是非常简单,让我看到中间步骤的进度,对于初学者来说并不难。很好的解决方案!
【解决方案3】:

您可以使用自联接:

SELECT User1.User, User1.Value as Name, User2.Value as Email,
  User3.Value as Phone
FROM Users User1
JOIN Users User2
  ON User2.User = User1.User
JOIN Users User3
  ON User3.User = User1.User
WHERE User1.Field = 'name' AND User2.Field = 'email' AND User3.Field = 'phone'
ORDER BY User1.User

我测试了这个查询,它有效。

【讨论】:

    【解决方案4】:

    我相信这将构建您正在寻找的结果集。从那里,您可以创建视图或使用数据填充新表。

    select user, name, email, phone from
    (select user, value as name from table where field='name')
    natural join
    (select user, value as email from table where field='email')
    natural join
    (select user, value as phone from table where field='phone')
    

    【讨论】:

      【解决方案5】:

      在 MySQL 中你可以这样做:

      SELECT
          id,
          group_concat(CASE WHEN field='name' THEN value ELSE NULL END) AS name,
          group_concat(CASE WHEN field='phone' THEN value ELSE NULL END) AS phone,
          ...
      FROM test
      GROUP BY id
      

      聚合函数其实无关紧要,只要每种类型只有一个字段即可。您也可以使用min()max() 来代替,效果相同。

      【讨论】:

      • 请注意,与连接解决方​​案相比,这只需要对整个数据集进行一次遍历,因此它应该更高效,并且性能不会随着添加更多字段而下降。
      【解决方案6】:

      Javier's answer 的变体,有我的投票权。

      SELECT 
        UserName.name, UserEmail.email, UserPhone.phone
      FROM 
        Users            AS UserName 
        INNER JOIN Users AS UserEmail ON UserName.User = UserEmail.User
          AND UserName.field = 'name' AND UserEmail.field = 'email'
        INNER JOIN Users AS UserPhone ON UserName.User = UserPhone.User
          AND UserPhone.field = 'phone'
      

      如果不能保证所有属性都存在,请使用LEFT JOINs(User,Field) 上的复合索引可能对此有益。

      【讨论】:

        猜你喜欢
        • 2022-09-22
        • 1970-01-01
        • 2019-12-26
        • 1970-01-01
        • 2020-04-05
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多