Reputation: 41
Is there anyway to 'compact' the results of a SQL query?
The results look like:
+----+-----------+----------+-----------+-------------+--------+
| ID | Firstname | Lastname | Hobby | Job | Age |
+----+-----------+----------+-----------+-------------+--------+
| 1 | John | Doe | (null) | (null) | 30 |
| 1 | John | Doe | Chess | (null) | (null) |
| 2 | Adam | Jackson | (null) | Accountant | (null) |
| 2 | Adam | Jackson | (null) | (null) | 55 |
| 3 | Michael | Smith | Knitting | (null) | (null) |
| 3 | Michael | Smith | (null) | Banker | (null) |
+----+-----------+----------+-----------+-------------+--------+
But I would like it to look like this:
+----+-----------+----------+-----------+-------------+--------+
| ID | Firstname | Lastname | Hobby | Job | Age |
+----+-----------+----------+-----------+-------------+--------+
| 1 | John | Doe | Chess | (null) | 30 |
| 2 | Adam | Jackson | (null) | Accountant | 55 |
| 3 | Michael | Smith | Knitting | Banker | (null) |
+----+-----------+----------+-----------+-------------+--------+
I tried using GROUP BY
but it only accepts the first custom field from the database for each person.
I've setup a SQL Fiddle here: http://sqlfiddle.com/#!9/39563/2
The setup is a little odd but I need to keep that structure due to the database I'm working on.
Upvotes: 0
Views: 581
Reputation: 60472
You should move the MAX-calculation into a Derived Table to apply aggregation before the Join. This will increase performance:
select p.id as "User ID", p.firstname Firstname, p.lastname as Lastname,
cf.Hobby,
cf.Job,
cf.Age
from
(
select cfv.relid,
max(case when cf.fieldname = "Hobby" then cfv.value end) as "Hobby",
max(case when cf.fieldname = "Job" then cfv.value end) as "Job",
max(case when cf.fieldname = "Age" then cfv.value end) as "Age"
from CustomFields as cf inner join CustomFieldValues as cfv
on cfv.fieldid = cf.id
where cf.fieldname = "Hobby"
or cf.fieldname = "Job"
or cf.fieldname = "Age"
group by cfv.relid
/* -- apply additional conditions here, e.g. "all three fields must be set"
having Hobby is not null
and Job is not null
and age is not null
*/
) as cf
inner join People as p
on cf.relid = p.id
order by p.id
Upvotes: 0
Reputation: 1998
Use the group_concat function in your group by function for string values and the max function for numeric fields.
SELECT id, FirstName, LastName,
GROUP_CONCAT(CASE WHEN Hobby IS NOT NULL THEN Hobby END) AS Hobby,
GROUP_CONCAT(CASE WHEN Job IS NOT NULL THEN Job END AS Job,
MAX(Age) AS Age
FROM TableGROUP BY id
Upvotes: 0
Reputation: 44891
You can use the max
aggregate function an group by
to flatten the result like this:
select
p.id as "User ID",
p.firstname Firstname,
p.lastname as Lastname,
max(case when cf.fieldname = 'Hobby' then cfv.value end) as "Hobby",
max(case when cf.fieldname = 'Job' then cfv.value end) as "Job",
max(case when cf.fieldname = 'Age' then cfv.value end) as "Age"
from CustomFields cf
join CustomFieldValues cfv on cfv.fieldid = cf.id
join People p on cfv.relid = p.id
where cf.fieldname in ('Hobby', 'Job', 'Age')
group by p.id, p.firstname, p.lastname
order by p.id;
Also, string literals should be enclosed in single-quotes, and you can use aliases to reduce the query text and make it more readable.
Upvotes: 2