How to match column names and values from joining and related tables? - mysql

I have the following DB structure:
cars:
id
make
features:
id
name
cars_feature:
id
car_id
feature_id
value
I want to be able to select all cars with all features and return results with feature.name as the column name and cars_feature.value as the value. Right now I am able to get all feature and all values but I only figured out how to do that with group_concat.
What I am looking for is the following output:
car_id car_make color wheels doors
1 Ford blue alloy
2 Audi alloy 3
Data example: SQLFiddle

The following is a modified version of code that I found at Combine multiple rows into one row MySQL.
SET #sql = NULL;
SELECT GROUP_CONCAT(DISTINCT CONCAT(
'MAX(CASE WHEN `name` = ''',
`name`,
''' THEN value END) AS `',
`name`, '`'
)
) INTO #sql
FROM (SELECT name,
value
FROM features, car_feature
WHERE features.id = car_feature.feature_id) AS car_features_feature;
SET #sql = CONCAT('SELECT car_feature.car_id AS car_id,
cars.make AS car_make,
',
#sql,
'
FROM car_feature
LEFT JOIN cars ON car_feature.car_id = cars.id
LEFT JOIN features ON car_feature.feature_id = features.id
GROUP BY car_id;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Thank you for the learning experience.

Related

mysql variants to columns from sql query

I have a mysql table which has for simplified purposes sku, and that sku has an unknown set of attribute_values
skus
------
id, sku, qty
1, abc, 10
2, bvcc, 20
variantvalues
-------------
id, sku_id, variant_id, value
1, 1, 1, red
1, 1, 2, medium
variants
--------
id, name
1, color
2, size
.....
EXPECTED RESULT
id, sku, color, size, qty
-------------------------
1, abc, red, medium, 10
the challenge I'm having is, can I do a query that gives me the data as a list of skus, and then columns for each of their variants and variant value... such that, if I added a new variant option... say, gender, to the variants table, and it was attributed to a sku, then there would be a new column for that?
The question is derived from this question: Modeling Product Variants
You are describing a dynamic pivot. Basically you need to dynamically generate the query string, using a sql query, then execute it.
Here is one way to do it in MySQL, using a prepared statement:
set #sql = null;
select group_concat(distinct
'max(case when v.name = ''', name, ''' then vv.value end) as `', name, '`'
) into #sql
from variants;
set #sql = concat(
'select s.id, s.sku, ',
#sql,
' from skus s
inner join variantvalues vv on vv.sku_id = s.id
inner join variants v on v.id = vv.variant_id
group by s.id, s.sku'
);
prepare stmt from #sql;
execute stmt;
deallocate prepare stmt;

How to make a pivot like sql result [duplicate]

I have following tables
demographic_categories
demographic_id demographic_name
1 color
2 age_group
project_tests
test_id group_id project_id
1 1 1
2 1 1
test_demographic_requirements
test_id project_id demgraphic_id demographic_value
1 1 1 blue
1 1 2 young
2 1 1 green
2 1 2 middle
And I need a query which would give me following result :
test_id group_id color age_group
1 1 blue young
2 1 green middle
I guess we need to use concept of pivot table to get the result, but I am unable to. And I demographic categories can we might be same they tend to change so I need something dynamic so what would be the best way to do it ?
I have tried doing the following based of previous similar questions but it doesn't seems to work for me, here is what I have tried:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when dc.demographic_name = ''',
demographic_name,
''' then trd.demographic_value end) AS ',
replace(demographic_name, ' ', '')
)
) INTO #sql
from demographic_categories;
SET #sql = CONCAT('SELECT pt.test_id, pt.group_id,
', #sql,'
from test_requirement_demographic trd
LEFT JOIN demographic_categories dc ON trd.demographic_id = dc.demographic_id
LEFT JOIN project_tests pt ON pt.test_id = trd.test_id and project_id =1
group by pt.test_id;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Your dynamic SQL is just fine except for one thing. It has an ambiguous column project_id on your second left join, Just replace it with pt.project_id or trd.project_id and it will give desired result.
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'max(case when dc.demographic_name = ''',
demographic_name,
''' then trd.demographic_value end) AS ',
replace(demographic_name, ' ', '')
)
) INTO #sql
from demographic_categories;
SET #sql = CONCAT('SELECT pt.test_id, pt.group_id,
', #sql,'
from test_demographic_requirements trd
LEFT JOIN demographic_categories dc ON trd.demographic_id = dc.demographic_id
LEFT JOIN project_tests pt ON pt.test_id = trd.test_id and pt.project_id =1
group by pt.test_id;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
I ran it on a rextester. Here is the link : dynamic_pivot_test

Group by and additional columns for each language/translation of product name

If I have these data in MySQL database:
PID LANG TRANS
1 EN Apple
1 DE Apfle
I want to get results in sql:
PID EN_TRANSLATION DE_TRANSLATION
1 Apple Apfle
How to do this? I already have tables and everything but need help writing query that will give me columns per language per result for each product.
NOTE: I don't know set of language(s) ! It can be EN, DE or whatever else..
Thx!
If you have known set of LANG then you can use the following technique to generate the pivot view
select
PID,
max(case when LANG = 'EN' then TRANS end) as EN_TRANSLATION,
max(case when LANG = 'DE' then TRANS end) as DE_TRANSLATION
from table_bame
group by PID
If the LANG values are unknown then you need to use dynamic sql for this as
set #sql = null;
select
group_concat(distinct
concat(
'max(case when LANG = ''',
LANG,
''' then TRANS end) AS ',
concat(LANG,'_TRANSLATION')
)
) into #sql
from table_name ;
set #sql = concat('select PID, ', #sql, ' from table_name
group by PID
');
prepare stmt from #sql;
execute stmt;
deallocate prepare stmt;
http://www.sqlfiddle.com/#!9/95d29/1

How to fetch row value as a column name In Mysql

I need help with below scenario. How can we get distinct values in rows as column Names dynamically?
I have data in below format..
Sno firstName Subject Marks
1 ABC Eng 10
2 PQR Hindi 20
3 LM Telgu 20
4 LM Hindi 20
5 LM Eng 39
I need output in below format.
Sno FirstName Eng Hindi Telgu
1 ABC 10 Null Null
2 PQR Null 20 Null
3 LM 39 20 20
If one more subject is added, query should be written dynamic enough to include those values too..
How can Write Query for This?
Hi there as Abecee told you in comment solution for your problem is pivoting table.
Here is query for your table
SELECT Sno, firstName,
SUM(CASE WHEN Subject = 'Eng' THEN Marks ELSE NULL END) AS Eng,
SUM(CASE WHEN Subject = 'Hindi' THEN Marks ELSE NULL END) AS Hindy,
SUM(CASE WHEN Subject = 'Telgu' THEN Marks ELSE NULL END) AS Telgu
FROM t1
GROUP BY firstName
ORDER BY Sno
And here is SQL Fiddle to see how it's work...
GL!
Edit:
Ok based on this bluefeet answer here is dynamics solution for your problem
SET #sql = NULL;
SELECT GROUP_CONCAT(DISTINCT
CONCAT('SUM(CASE WHEN Subject = ''',
Subject,
''' THEN Marks ELSE NULL END) AS ',
REPLACE(Subject, ' ', ''))) INTO #sql
FROM t1;
SET #sql = CONCAT('SELECT Sno, firstName, ', #sql, '
FROM t1
GROUP BY firstName
ORDER BY Sno');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
First part of this
SET #sql = NULL;
SELECT GROUP_CONCAT(DISTINCT
CONCAT('SUM(CASE WHEN Subject = ''',
Subject,
''' THEN Marks ELSE NULL END) AS ',
REPLACE(Subject, ' ', ''))) INTO #sql
FROM t1;
is to dynamically create this part of first query I wrote
SUM(CASE WHEN Subject = 'Eng' THEN Marks ELSE NULL END) AS Eng,
SUM(CASE WHEN Subject = 'Hindi' THEN Marks ELSE NULL END) AS Hindy,
SUM(CASE WHEN Subject = 'Telgu' THEN Marks ELSE NULL END) AS Telgu
the second part is to create prepared statement and to include dynamically created part into this
SELECT Sno, firstName,
-- here you add dynamically created part #sql
FROM t1
GROUP BY firstName
ORDER BY Sno
you do that with CONCAT in this part of code
SET #sql = CONCAT('SELECT Sno, firstName, ', #sql, '
FROM t1
GROUP BY firstName
ORDER BY Sno');
when you create that from that string you prepare statement and execute it
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Hope that is a little bit clear now...
Here is SQL Fiddle so you can see that you can compare both query and see how it's work..
GL!
P.S. if you have any question fill free to ask in comment bellow

Pivoting table only returns 1 row

My SQLFiddle: http://sqlfiddle.com/#!2/729a9/1
As you can see, despite there being two rows in the table, there is one row returned.
It's also the highest id, so maybe that has something to do with it?
I'm stumped like a log, sorry to say.
SQL:
SELECT GROUP_CONCAT(distinct
CONCAT(
'max(case when `pat`.`name` = ''',
`pat`.name,
''' then `pa`.`value` end) as `',
`pat`.name, '`'
)
) INTO #list
FROM `product_attribute_types` pat;
SET #sql = CONCAT('select ', #list, '
from `products` `p`
LEFT JOIN `product_attributes` `pa`
ON `p`.id=`pa`.`product_id`
LEFT JOIN `product_attribute_types` `pat`
ON `pa`.`type`=`pat`.`id`
');
PREPARE stmt FROM #sql;
EXECUTE stmt;
Firstly: you have had two attributes for the same product_id = 1, change table product_attributes in this way -
INSERT INTO `product_attributes` (`product_id`,`type`,`value`) VALUES
(1,1,'blue'),
(1,2,'shirt'),
(2,1,'green'),
(2,2,'pants');
Then try this one -
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'MAX(IF(pat.name = ''', name, ''', pa.value, NULL)) AS ', name
)
) INTO #sql
FROM product_attribute_types;
SET #sql = CONCAT('SELECT pa.product_id, ', #sql, ' FROM product_attributes pa INNER JOIN product_attribute_types pat ON pa.type = pat.id GROUP BY pa.product_id');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Result:
+------------+-------+-------+
| product_id | color | name |
+------------+-------+-------+
| 1 | blue | shirt |
| 2 | green | pants |
+------------+-------+-------+
Add WHERE filter if needed.
You're missing a GROUP BY clause in #sql, as well as the product ID that the row refers to.
SET #sql = CONCAT('select p.id, ', #list, '
from `products` `p`
LEFT JOIN `product_attributes` `pa`
ON `p`.id=`pa`.`product_id`
LEFT JOIN `product_attribute_types` `pat`
ON `pa`.`type`=`pat`.`id`
GROUP BY p.id
');
FIDDLE