Sorting columns with pivot table in MySQL - mysql

I would like to sort my columns in pivot table. Here is a link to my example table and query what i have now. As you can see in result the name of the columns are unsorted.
I'm basically doing this:
SELECT GROUP_CONCAT(DISTINCT
CONCAT('MAX(CASE WHEN DATE(date) = ''', date,
''' THEN score END) `', DATE_FORMAT(date,'%d.%m.%Y'), '`'))
INTO #sql
FROM tabletest
ORDER BY date;
SET #sql = CONCAT('SELECT name,', #sql, '
FROM tabletest
GROUP BY name');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
My column output is like this:
| NAME | 30.11.2013 | 28.11.2013 | 27.11.2013 | 29.11.2013 |
|---------|------------|------------|------------|------------|
| Adele | 234 | 552 | (null) | (null) |
And I would like to have the columns sorted.
Thanks in advance.

Just add an ORDER BY date inside the GROUP_CONCAT:
SELECT GROUP_CONCAT(DISTINCT
CONCAT('MAX(CASE WHEN DATE(date) = ''', date,
''' THEN score END) `', DATE_FORMAT(date,'%d.%m.%Y'), '`')
ORDER BY date)

Related

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

COLUMN NAME and COLUMN COMMENT from one table and COLUMN VALUE from another. How?

I have a table called tbl_mainsheet7 created like this:
pk_mainsheet client_id project_id mainsheet_id project_cat EA_WTRESRVD EA_WTRESRV EA_FEEASBT
------------ --------- ---------- ------------ ----------- ----------- ---------- ----------
1 111 222 333 3 0 0 0
2 11 22 33 3 0 0 0
MySQL INFORMATION_SCHEMA.COLUMNS Query for tbl_mainsheet7 created like this:
SELECT `COLUMN_NAME`, `COLUMN_COMMENT` FROM INFORMATION_SCHEMA.COLUMNS WHERE `TABLE_NAME` = 'tbl_mainsheet7'
..returning this:
COLUMN_NAME COLUMN_COMMENT
------------- ------------------------------------------------------
pk_mainsheet
client_id
project_id
mainsheet_id
project_cat
EA_WTRESRVD EMERGENCY SERVICE CALL
EA_WTRESRV EMERGENCY SERVICE CALL AFTER HRS
EA_FEEASBT ASBESTOS TEST FEE
How can I...
SELECT COLUMN_NAME, COLUMN_VALUE, COLUMN_COMMENT FROM ... WHERE...
Maybe a JOIN? I am really scratching my head.
UPDATE
So I got this to work but for a single predetermined column only. How can I use a variable to make this dynamic?
Like replacing WTRESRVD with a variable relating to COLUMN_NAME
SELECT COLUMN_NAME, (SELECT EA_WTRESRVD FROM tbl_mainsheet7 WHERE client_id = '111') AS COLUMN_VALUE, COLUMN_COMMENT FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'tbl_mainsheet7'
I've adapted the solution from this answer for your needs with some changes: 1) I put the pk_mainsheet as an identifier of rows in the target tables 2) I discovered a length issue with the #sql variable, there seems to be a limitation in the result, when you need more then the columns as in table tbl_mainsheet7 now. Hope that helps.
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'select pk_mainsheet, ''',
c.column_name,
''' as COLUMN_NAME, ',
c.column_name,
' as COLUMN_VALUE, ''',
c.column_comment,
''' as COLUMN_COMMENT from tbl_mainsheet7'
) SEPARATOR ' UNION ALL
'
) INTO #sql
FROM information_schema.columns c
where c.table_name = 'tbl_mainsheet7'
and c.column_name <> 'pk_mainsheet'
order by c.ordinal_position;
-- INTO #sql
SET #sql
= CONCAT('select COLUMN_NAME,COLUMN_VALUE,COLUMN_COMMENT
from
(', #sql, ') x WHERE Pk_mainsheet = 1 ');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

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

MYSQL two columns that have value in first column how can i do an inner join

Hi I have a table that looks like this
dt ticker open
1 A 1
1 B 3
2 A 1.1
2 B 2.5
I would need the result to look like
dt A B
1 1 3
2 1.1 2.5
My current query I have included below gets me
dt A B
1 1 NULL
1 NULL 3
2 1.1 NULL
2 NULL 2.5
if anyone could help me out that would be very much appreciated
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'(IF(ticker = ''',
ticker,
''', open, NULL)) AS ''',
ticker,''''
)
) INTO #sql
FROM
prices;
SET #sql = CONCAT('SELECT dt, ', #sql, ' FROM prices');
-- SET #sql = CONCAT('SELECT dt, ', #sql, ' FROM prices GROUP BY dt');
PREPARE stmt FROM #sql;
EXECUTE stmt;
One way to get the result would be:
SELECT t.dt
, MAX(IF(t.ticker='A',t.open,NULL)) AS A
, MAX(IF(t.ticker='B',t.open,NULL)) AS B
FROM mytable t
GROUP BY t.dt
(In MySQL the MAX aggregate can actually be omitted, thought an aggregate is required in other DBMS.)
SELECT t.dt
, IF(t.ticker='A',t.open,NULL) AS A
, IF(t.ticker='B',t.open,NULL) AS B
FROM mytable t
GROUP BY t.dt
Another approach:
SELECT t.dt
, t.open AS A
FROM mytable t
LEFT
JOIN (SELECT s.dt
, t.open AS B
FROM mytable s
WHERE s.ticker = 'B'
GROUP BY s.dt
) b
ON b.dt = t.dt
WHERE t.ticker = 'A'
GROUP BY t.dt
ORDER BY t.dt
You need to add Max to your Group_Concat, Try this
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'Max(case when ticker = ''',
ticker,
''' then open end) AS ',
replace(ticker, ' ', '')
)
) INTO #sql
from prices;
SET #sql = CONCAT('SELECT x.dt, ', #sql, ' from prices x
group by x.dt');
PREPARE stmt FROM #sql;
EXECUTE stmt;
SQL Fiddle Demo
try it
select a.dt,a.A,CASE WHEN ticker='B' THEN open END AS 'B' from (SELECT dt,CASE WHEN ticker='A' THEN open END AS 'A' FROM test group by dt) a inner join test using(dt) where CASE WHEN ticker='B' THEN open END is not null;
result
+------+-------------------+------+
| dt | A | B |
+------+-------------------+------+
| 1 | 1 | 3 |
| 2 | 1.100000023841858 | 2.5 |
+------+-------------------+------+
Try this:
SELECT GROUP_CONCAT(CONCAT(" MAX(IF(ticker = '", ticker, "', open, NULL)) AS ", ticker)) INTO #sql
FROM prices;
SET #sql = CONCAT('SELECT dt, ', #sql, ' FROM prices GROUP BY dt');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
Check this SQL FIDDLE DEMO
OUTPUT
| DT | A | B |
------------------
| 1 | 1 | 3 |
| 2 | 1.1 | 2.5 |