如何在 mysql 中拆分/取消透视 group_concat?

Posted

技术标签:

【中文标题】如何在 mysql 中拆分/取消透视 group_concat?【英文标题】:How to split / unpivot group_concat in mysql? 【发布时间】:2015-06-12 13:22:36 【问题描述】:

我目前有一个视图,其中有一个名为 alt_email_contact 的列,我使用 group_concat 函数来获取与一个联系人关联的多封电子邮件。但是,我希望能够提取每封电子邮件并为每封电子邮件创建一个单独的列。

例子:

id    email
 1     SkyW@gmail.com, SW@gmail.com, WW@gmail.com, WalterW@gmail.com 

电子邮件的数量可能会从一个用户更改为另一个用户,因此每个用户不会总是有四封电子邮件。我想为每封电子邮件创建一个新列,如下所示:

id      email_1         email_2        email_3        email_4 
1       SkyW@gmail.com  SW@gmail.com   WW@gmail.com   WalterW@gmail.com 

(我正在使用 phpmyadmin)我希望能够修改我的视图以包含每个用户的可变数量的电子邮件。

【问题讨论】:

你为什么要这样做?将每封备用电子邮件放在自己的行中有什么问题(即根本没有group_concat)?这比拥有X 列数(在进行初始查询之前不知道X)更具可扩展性。 SQL 查询具有固定数量的列。您想要多少封电子邮件? @MikeBrant 我将这些字段导入另一个数据库,为此我必须将一个字段映射到另一个字段。因此,我需要与一个联系人关联的所有电子邮件都在一行中(并且每封电子邮件都在单独的列中)。 @GordonLinoff 我想要 5 封电子邮件,但可以少于 5 封 @dantheman 在目标数据库中,为什么需要将这些值全部放在一行中?你本质上试图做的不是关系数据库要做的事情。 【参考方案1】:

您可以使用substring_index() 来实现您想要的。它不是很漂亮,但它会起作用:

select id,
       substring_index(emails, ', ', 1) as email_1
       (case when length(emails) - length(replace(emails, ',', '')) >= 1
             then substring_index(substring_index(emails, ', ', 2), ', ', -1)
        end) as email_2,
       (case when length(emails) - length(replace(emails, ',', '')) >= 2
             then substring_index(substring_index(emails, ', ', 3), ', ', -1)
        end) as email_3,
       (case when length(emails) - length(replace(emails, ',', '')) >= 3
             then substring_index(substring_index(emails, ', ', 4), ', ', -1)
        end) as email_4,
       (case when length(emails) - length(replace(emails, ',', '')) >= 4
             then substring_index(substring_index(emails, ', ', 5), ', ', -1)
        end) as email_5     
from table t;

如果您愿意,可以将这些值插入到另一个表中。

【讨论】:

@GordonLiff,当我尝试实现这个查询时,它给了我重复,例如,如果一个联系人只有两封电子邮件,它将显示:email_1:w@gmail.com,email_2:ww @gmail.com,email_3:ww@gmail.com,email_4:ww@gmail.com,email_5:ww@gmail.com。我该如何解决这个问题? 对于输出的每一列,您需要更深地嵌套 substring_index() 调用。 我总是需要 5 列。 @dantheman 。 . . when 比较中出现错误。【参考方案2】:

不要使用那个视图,因为GROUP_CONCAT() 已经破坏了规范化。您想使用 mysql 有限的 SQL 功能来模拟数据透视表。

假设您的视图基于如下所示的Contacts 表:

CREATE TABLE Contacts
( id INTEGER NOT NULL
, alt_email_contact VARCHAR(256) NOT NULL
);

改为创建这个帮助视图(基本上是 RANK() OVER (PARTITION BY id ORDER BY alt_email_contact),除了 MySQL 不支持 RANK()):

CREATE VIEW NumberedContacts AS
    SELECT c1.id, c1.alt_email_contact, COUNT(*) AS rank
        FROM Contacts c1
            INNER JOIN Contacts c2
                ON c2.id = c1.id AND
                   c1.alt_email_contact >= c2.alt_email_contact
        GROUP BY c1.id, c1.alt_email_contact;

然后您可以编写此查询或视图,它会为您提供最多 5 个备用电子邮件地址,按字母顺序排列:

CREATE VIEW ContactsForImport AS
    SELECT c1.id
         , c1.alt_email_contact AS email_1
         , c2.alt_email_contact AS email_2
         , c3.alt_email_contact AS email_3
         , c4.alt_email_contact AS email_4
         , c5.alt_email_contact AS email_5
        FROM NumberedContacts AS c1
            LEFT OUTER JOIN NumberedContacts AS c2
                ON c1.id = c2.id AND c2.rank = 2
            LEFT OUTER JOIN NumberedContacts AS c3
                ON c1.id = c3.id AND c3.rank = 3
            LEFT OUTER JOIN NumberedContacts AS c4
                ON c1.id = c4.id AND c4.rank = 4
            LEFT OUTER JOIN NumberedContacts AS c5
                ON c1.id = c5.id AND c5.rank = 5
        WHERE c1.rank = 1;

SQL Fiddle

【讨论】:

谢谢!这看起来正是我所需要的,但是有什么方法可以让我以与您选择视图相同的方式创建视图?我需要视图实际上具有与选择相同的结构。 不确定是什么问题。您可以使用CREATE VIEW ViewName AS SELECT … 对任何SELECT 进行查看。 啊好吧好吧!我不确定,因为这里有单独的语句。 可能有误传。 NumberedContacts 视图只是一个中间视图,用于构建您真正想要的视图。

以上是关于如何在 mysql 中拆分/取消透视 group_concat?的主要内容,如果未能解决你的问题,请参考以下文章

在 MySQL 中取消透视多个列

如何在 mysql 中取消透视数据

如何按2列取消透视sql表[重复]

怎么全部取消excel2003数据透视表中的分类汇总

如何在 Oracle 11 中取消透视单行?

如何对 Eloquent 关系中的数据透视表列进行 GROUP 和 SUM?