简体   繁体   中英

MYSQL Subquery Rows as Columns in Main Query

In our database we have a user table, and key-value tables for other data. We have been trying to come up with a query that will join the two, taking the keys from the kv table as column headings, and the values as fields.

Our only solution as of now is to GROUP_CONCAT the key-value pairs for each user as a column, and then parse them after the query has been output—slow and bad...

Here is the general setup:

User.db Tables:

------------------------------
| uid | firstname | lastname |
------------------------------
| 01 | john       | doe      |
| 02 | jane       | doe      |
------------------------------

-----------------------------
| uid | question  |  answer |
-----------------------------
|  01 | question1 | answer1 |
|  01 | question2 | answer2 |
|  02 | question1 | answer3 |
|  02 | question2 | answer4 |
-----------------------------

The query result we'd like to get:

------------------------------------------------------
| uid | firstname | lastname | question1 | question2 |
------------------------------------------------------
|  01 | john      | doe      | answer1   | answer2   |
|  02 | jane      | doe      | answer3   | answer4   |
------------------------------------------------------

I'm hoping there's a straightforward way to do this, but haven't been able to find anything. All help will be greatly appreciated.

In other database's you could use a PIVOT function but MySQL does not have that function so will have to replicate it using an aggregate function and a CASE statement. If you know all of the values, you can hard-code the values similar to this :

select u.uid, u.firstname, u.lastname,
  max(case when question='question1' then answer else null end) as question1,
  max(case when question='question2' then answer else null end) as question2
from users u 
left join kv 
  on u.uid = kv.uid
group by u.uid, u.firstname, u.lastname;

See SQL Fiddle with demo

But if you have unknown values, then you can use a prepared statement to generate dynamic SQL:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'max(case when question = ''',
      question,
      ''' then answer else NULL end) AS ',
      question
    )
  ) INTO @sql
FROM kv;

SET @sql = CONCAT('SELECT u.uid, u.firstname, u.lastname, ', @sql, ' 
                  from users u 
                  left join kv 
                    on u.uid = kv.uid 
                  group by u.uid, u.firstname, u.lastname');

PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

See SQL Fiddle with Demo

Both versions will generate the same result:

| UID | FIRSTNAME | LASTNAME | QUESTION1 | QUESTION2 |
------------------------------------------------------
|   1 |      john |      doe |   answer1 |   answer2 |
|   2 |      jane |      doe |   answer3 |   answer4 |

The benefit of the prepared statement is that if you have changing values, then this will generate the list of columns at run-time.

This would simulate a PIVOT table:

Select
  uid,
  firstname,
  lastname,
  max(case when question = 'question1' then answer end) as question1,
  max(case when question = 'question2' then answer end) as question2
From
  users inner join answers on users.uid = answers.uid
Grop by uid, firstname, lastname

There's also a solution with joins:

Select uid, firstname, lastname,
  answers_1.answer as question1,
  answers_2.answer as question2
From
  users left join answers answers_1
  on users.uid = answers_1.uid and answers_1.question = 'question1'
  left join answers answers_2
  on users.uid = answers_2.uid and answers_2.question = 'question2'

Of course, you have to know in advance what the questions are. If that's not the case, as far as I know, since MySql doesn't support PIVOT there's no way to answer your question using just standard SQL.

you can try something like that:

select u.uid, u.firstname, u.lastname,
  max(case when question="question1" then answer else null) as question1,
  max(case when question="question2" then answer else null) as question2
from user u join answers a on u.uid = a.uid
group by u.uid, u.firstname, u.lastname

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