简体   繁体   中英

How to split / unpivot group_concat in mysql?

I currently have a view that has a column called alt_email_contact and I used the group_concat function in order to get multiple emails associated with one contact. However I want to be able to extract each email and create a separate column for each.

Example:

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

the amount of emails is subject to change from one user to another therefore there wont always be four emails for each user. I want to create a new column per each email like so:

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

(I am using phpmyadmin) I would like to be able to modify my view to contain the variable amount of emails per user.

You can use substring_index() to achieve what you want. It is not really pretty, but it will work:

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;

You can insert these values into another table, if you like.

Don't use that view, since GROUP_CONCAT() has already ruined the normalization. You want to simulate a pivot table using the limited SQL capabilities of MySQL.

Let's assume that your view is based on a Contacts table that looks like this:

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

Create this helper view instead (which is basically RANK() OVER (PARTITION BY id ORDER BY alt_email_contact) , except that MySQL doesn't support 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;

Then you can write this query or view, which gives you up to 5 alternate e-mail addresses, ordered alphabetically:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM