Reputation: 1868
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 k-v 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.
Upvotes: 1
Views: 4174
Reputation: 247850
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;
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;
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.
Upvotes: 4
Reputation: 49089
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.
Upvotes: 1
Reputation: 12837
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
Upvotes: 1