29

I have a legacy table of user information (that is still in active use) and I cannot change the structure of -

id    name       value
------------------------------
0     timezone   Europe/London
0     language   en
0     country    45
0     something  x
1     timezone   Europe/Paris
1     language   fr
1     country    46

timezone/language/country etc are only examples of names, they can be variable/there is no finite list other than unique on rows of that column

I need a MySQL compatible SQL query that would return -

id    timezone       language    country  something
---------------------------------------------------
0     Europe/London  en          45       x
1     Europe/Paris   fr          46

I've looked through various answers on stackoverflow around hacking Pivot table functionality in to MySQL, and similar but none of them seem to match this case of using variable column name alias from unique row values from a column of the same table. Although I have had little sleep so they're all starting to become a bit of a blur, apologies in advance.

Closest I could find would be to use prepared statements https://stackoverflow.com/a/986088/830171 which would first get all possible/unique values from name column and build a query that uses CASE WHEN, and/or multiple sub-SELECT or JOIN on same table queries.

The alternatives I can think of would be to get all rows for that user id and process them in the application itself in a for-loop, or attempt to limit the names to a finite amount and use sub-SELECTs/JOINs. However that second option is not ideal if a new name is added I'd have to revisit this query.

Please tell me I've missed something obvious

Community
  • 1
  • 1
gingerCodeNinja
  • 1,239
  • 1
  • 12
  • 27
  • Do you really need this to be expressed in `sql` or would post-processing suffice with your programming language of choice? If you need to join against your de-fungled data, then a view/proc might be the trick. – bluevector Jun 07 '12 at 04:39
  • Yeh I'll keep the mess in the application level for now - I'll get all rows for that user id and loop over putting data in an associative array. I just wanted to check that was the only good option. Still very handy to have the SQL method for migrations and one-off reports. – gingerCodeNinja Jun 07 '12 at 07:33

1 Answers1

50

Unlike some other RDBMS MySQL doesn't have native support for pivoting operations of this sort by design (the developers feel it's more suited to the presentation, rather than database, layer of your application).

If you absolutely must perfom such manipulations within MySQL, building a prepared statement is the way to go—although rather than messing around with CASE, I'd probably just use MySQL's GROUP_CONCAT() function:

SELECT CONCAT(
  'SELECT `table`.id', GROUP_CONCAT('
     ,    `t_', REPLACE(name, '`', '``'), '`.value
         AS `', REPLACE(name, '`', '``'), '`'
     SEPARATOR ''),
 ' FROM `table` ', GROUP_CONCAT('
     LEFT JOIN `table`   AS `t_', REPLACE(name, '`', '``'), '`
            ON `table`.id = `t_', REPLACE(name, '`', '``'), '`.id
           AND `t_', REPLACE(name, '`', '``'), '`.name = ', QUOTE(name)
     SEPARATOR ''),
 ' GROUP BY `table`.id'
) INTO @qry FROM (SELECT DISTINCT name FROM `table`) t;

PREPARE stmt FROM @qry;
EXECUTE stmt;

See it on sqlfiddle.

Note that the result of GROUP_CONCAT() is limited by the group_concat_max_len variable (default of 1024 bytes: unlikely to be relevant here unless you have some extremely long name values).

eggyal
  • 122,705
  • 18
  • 212
  • 237
  • 4
    wow - thanks for going to all the effort writing and testing out a solution. Much appreciated. I've always wanted to know how to make this type of query, and it looks as complicated as I imagined it would be. For now I'll keep the messiness in the application level, but this query will still come in very handy for reporting and migrations. Thanks. – gingerCodeNinja Jun 07 '12 at 06:53
  • I have a very similar scenario and I get the pivoted result displayed alright, using your excellent example. But I need the result to be stored in a table as I want to generate a chart from it. Is it possible to store the pivoted result into a table? Also, is it possible to execute above in Php? My source table is user, date, performance and pivot is user as rows, date as columns and performance as cell values. – Sri Jun 15 '13 at 20:00
  • Have put my example table on sqlfiddle (please see http://sqlfiddle.com/#!2/06be4/1) – Sri Jun 15 '13 at 20:30
  • Very nicely done, the above query helped me correct and expand a query I was working on. thank you! – Baber Jun 05 '14 at 23:43