Reputation: 2030
I am trying to write a query to create a 'table' of data as follows:
SELECT cs.`category_id`, cs.`ProcessDate`, cs.`PercentChange`
FROM `Category_Statistics` cs
WHERE cs.`ProcessDate` >= '2011-05-10'
AND cs.`ProcessDate` <= '2011-05-14'
Which would return something like:
CategoryId | ProcessDate | PercentChange
-------------------------------------------
category_4 | 2011-05-10 | 10
category_4 | 2011-05-11 | 18
category_4 | 2011-05012 | 12
...
category_7 | 2011-05-10 | 21
category_7 | 2011-05-11 | 7
...
category_12 | 2011-05-10 | 7
category_12 | 2011-05-11 | 15
Now I want the results to be something like this (from a MySQL query, not manipulated by the app):
CategoryId | 2011-05-10 | 2011-05-11 | 2011-05-12 | 2011-05-13 | 2011-05-14 |
--------------------------------------------------------------------------------
category_4 | 10 | 18 | 12 | 9 | 14 |
category_7 | 21 | 7 | 16 | 14 | 13 |
categeory_12 | 7 | 15 | 11 | 19 | 8 |
--------------------------------------------------------------------------------
There are two caveats to this:
The date range can grow or shrink (depending on the query)
PercentChange may be null in some cases (lets say category_7 / 2011-05-12 may not have a value set)
So ultimately I am not quite sure how to build the select part of the query to reflect a dynamic number of columns (I know it has something to do with CONCAT).
Edit --> Partial working code -->
SELECT `CategoryId`,
MAX(IF(c.`ProcessedOn` = '2011-04-20', c.`PercentChange`, NULL)) AS '2011-04-20',
MAX(IF(c.`ProcessedOn` = '2011-04-21', c.`PercentChange`, NULL)) AS '2011-04-21',
MAX(IF(c.`ProcessedOn` = '2011-04-22', c.`PercentChange`, NULL)) AS '2011-04-22',
MAX(IF(c.`ProcessedOn` = '2011-04-23', c.`PercentChange`, NULL)) AS '2011-04-23',
MAX(IF(c.`ProcessedOn` = '2011-04-24', c.`PercentChange`, NULL)) AS '2011-04-24'
FROM `Category_Gravity` c
WHERE c.`ProcessedOn` >= '2011-04-20'
AND c.`ProcessedOn` <= '2011-04-24'
GROUP BY `CategoryId`
What I need to do now is turn the
MAX(IF(c.`ProcessedOn` = '2011-04-20', c.`PercentChange`, NULL)) AS '2011-04-20',
into something more dynamic (as I the date ranges will change)
Upvotes: 1
Views: 9888
Reputation: 56357
Take a look at this similar thread where I wrote a sp to accomplish the task
edit. Updated answer
create table `pivot` (
`id` int(11) not null auto_increment,
`categoryid` int(11) default null,
`processdate` date default null,
`percentchange` int(11) default null,
primary key (`id`)
) engine=myisam auto_increment=9 default charset=latin1;
/*Data for the table `pivot` */
insert into `pivot`(`id`,`categoryid`,`processdate`,`percentchange`) values (1,4,'2011-05-10',1);
insert into `pivot`(`id`,`categoryid`,`processdate`,`percentchange`) values (2,4,'2011-05-11',22);
insert into `pivot`(`id`,`categoryid`,`processdate`,`percentchange`) values (3,4,'2011-05-12',3);
insert into `pivot`(`id`,`categoryid`,`processdate`,`percentchange`) values (4,7,'2011-05-10',4);
insert into `pivot`(`id`,`categoryid`,`processdate`,`percentchange`) values (5,7,'2011-05-11',5);
insert into `pivot`(`id`,`categoryid`,`processdate`,`percentchange`) values (6,12,'2011-05-10',6);
insert into `pivot`(`id`,`categoryid`,`processdate`,`percentchange`) values (7,12,'2011-05-12',7);
insert into `pivot`(`id`,`categoryid`,`processdate`,`percentchange`) values (8,4,'2011-05-13',12);
delimiter //
drop procedure if exists dynamic_view2//
create procedure dynamic_view2(in sdate date,in edate date)
begin
declare finish int default 0;
declare cdate date;
declare str varchar(10000) default "select categoryid,";
declare curs cursor for select processdate from pivot where processdate between sdate and edate group by processdate;
declare continue handler for not found set finish = 1;
open curs;
my_loop:loop
fetch curs into cdate;
if finish = 1 then
leave my_loop;
end if;
set str = concat(str, "max(case when processdate = '",cdate,"' then percentchange else null end) as `",cdate,"`,");
end loop;
close curs;
set str = substr(str,1,char_length(str)-1);
set @str = concat(str," from pivot
group by categoryid");
prepare stmt from @str;
execute stmt;
deallocate prepare stmt;
end;//
delimiter ;
mysql> call dynamic_view2('2011-05-10','2011-05-13');
+------------+------------+------------+------------+------------+
| categoryid | 2011-05-10 | 2011-05-11 | 2011-05-12 | 2011-05-13 |
+------------+------------+------------+------------+------------+
| 4 | 1 | 22 | 3 | 12 |
| 7 | 4 | 5 | NULL | NULL |
| 12 | 6 | NULL | 7 | NULL |
+------------+------------+------------+------------+------------+
3 rows in set (0.00 sec)
Upvotes: 5