Reputation: 527
I have a the following MySQL tables (MySQL 5.6):
contacts
+----+----------------+
| id | email |
+----+----------------+
| 1 | [email protected] |
| 2 | [email protected] |
+----+----------------+
contact_attributes
+----+-------------+
| id | name |
+----+-------------+
| 1 | name |
| 2 | current_car |
| 3 | car_color |
+----+-------------+
contact_attribute_values
+----+------------+-------------------------+------------+
| id | value | contact_attribute_value | contact_id |
+----+------------+-------------------------+------------+
| 1 | John Doe | 1 | 1 |
| 2 | Eva Milton | 1 | 2 |
| 3 | BMW X3 | 2 | 1 |
| 4 | Volvo XC90 | 2 | 2 |
| 5 | Pink | 3 | 1 |
| 6 | Blue | 3 | 2 |
+----+------------+-------------------------+------------+
Is there a way to create a temporary table with the following schema in MySQL to fetch and filter the contacts by attributes?
+------------+----------------+------------+-------------+-----------+
| contact_id | contact_email | name | current_car | car_color |
+------------+----------------+------------+-------------+-----------+
| 1 | [email protected] | John Doe | BMW X3 | Pink |
| 2 | [email protected] | Eva Milton | Volvo XC90 | blue |
+------------+----------------+------------+-------------+-----------+
Thanks for your help
Upvotes: 0
Views: 353
Reputation: 522712
You could create a view which generates this result:
CREATE VIEW contacts_view AS
SELECT
c.id,
c.email, -- OK to select this since we GROUP BY the primary key
MAX(CASE WHEN ca.name = 'name' THEN cav.value END) AS name,
MAX(CASE WHEN ca.name = 'current_car' THEN cav.value END) AS current_car,
MAX(CASE WHEN ca.name = 'car_color' THEN cav.value END) AS car_color
FROM contacts c
LEFT JOIN contact_attribute_values cav
ON c.id = cav.contact_id
INNER JOIN contact_attributes ca
ON cav.contact_attribute_value = ca.id
GROUP BY
c.id;
You could create a temporary table which has the output from the above view. But I sense that a view is more of what you are after here. A temporary table would be deleted after your session ends, which means you have to populate again each time. And, the data inside would get stale as soon as the data in your underlying tables changes.
Upvotes: 3
Reputation:
Use CASE
expression like this:
SELECT
v.contact_id,
MAX(CASE WHEN a.name = 'contact_email' THEN v.value ELSE '' END) AS 'contact_email',
MAX(CASE WHEN a.name = 'name' THEN v.value ELSE '' END) AS 'name',
MAX(CASE WHEN a.name = 'current_car' THEN v.value ELSE '' END) AS 'current_car'
MAX(CASE WHEN a.name = 'car_color' THEN v.value ELSE '' END) AS 'car_color'
FROM contacts as c
INNER JOIN contact_attribute_values as v on c.id = v.contact_id
INNER JOIN contact_attributes as a on v.contact_attribute_value = a.id
group by v.contact_id;
And you don't have to write down all the attributes, you can make it dynamically like this:
SET @cols = NULL;
SET @sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT CONCAT('MAX(IF(a.name = ''',
a.name, ''', v.value , NULL)) AS ', '''', a.name , '''')
) INTO @cols
FROM contact_attributes a;
SET @sql = CONCAT('SELECT
v.contact_id,', @cols, '
FROM contacts as c
LEFT JOIN contact_attribute_values as v on c.id = v.contact_id
INNER JOIN contact_attributes as a on v.contact_attribute_value = a.id
group by v.contact_id;');
prepare stmt
FROM @sql;
execute stmt;
Upvotes: 1