1

My existing table is like this:

ID   Grant   Subtotal       Month
1   Hatch   144.56         Nov-2012
2   NIH     240.9          Nov-2012
3   NSF     100.7          Nov-2012
4   Other   276.67         Nov-2012
5   NSF     234.53         Oct-2012
6   DOE     214.35         Oct-2012
7   Hatch   321.54         Oct-2012
8   Other   312.35         Oct-2012
9   NSF     156.89         Sep-2012
10  Hatch   147.99         Sep-2012
11  Other   245.67         Sep-2012
12  State   148.66         Sep-2012

The table lists monthly expenses on each grant. As time goes by, the row number is increasing. Within the grant column, the names remain the same most of the time. When a new grant is available, it will appear here. This happens not very often.

Now I want to plot the data for a chart so people can see how much is spent each month for each grant. For this purpose, I would like to reshape the table like this:

ID   Month          DOE   Hatch     NIH     NSF     Other      State
1   Nov-2012             144.56   240.9    100.7    276.67  
2   Oct-2012     214.35  321.54           234.53    312.35  
3   Sep-2012             147.99           156.89    245.67     148.66

The desired new table keeps column Month (distinct) but will use all the data values of the Grant column (distinct) in the original table as column names (dynamic). These new columns take the corresponding data values of column Subtotal as their data.

Could someone help build the query? Thanks a lot.

BTW: I am using MySQL/PHP. I am a newbie. Any sample code is highly appreciate.

Taryn
  • 242,637
  • 56
  • 362
  • 405
Jeff Lee
  • 19
  • 2
  • 1
    A 'pivot' or 'crosstab' function would help here. What DBMS system are you using, as these functions are vendor specific – PinnyM Nov 12 '12 at 17:58
  • 1
    Or, if you don't have a `pivot` function available, you can just do a bunch of self joins (one for each aggregation column). –  Nov 12 '12 at 18:12
  • I am by no means a coder. could any of you provide any sample code? Thanks. – Jeff Lee Nov 12 '12 at 18:19
  • Thanks. This is my first post on Stack Overflow. – Jeff Lee Nov 12 '12 at 18:23
  • 1
    @JeffLee - You're welcome. [Try something](http://whathaveyoutried.com), and if you get stuck, let us know what you tried. –  Nov 12 '12 at 18:25
  • possible duplicate of [MySQL pivot row into dynamic number of columns](http://stackoverflow.com/questions/12004603/mysql-pivot-row-into-dynamic-number-of-columns) – RichardTheKiwi May 03 '13 at 09:01

1 Answers1

1

This is basically a PIVOT but MySQL does not have PIVOT function. So you will want to replicate this using an aggregate function and a CASE statement. If you know the number of Grant values that you have then you can hard-code the query similar to this:

select 
  Month,
  sum(case when `grant`='DOE' then subtotal else 0 end) DOE,
  sum(case when `grant`='Hatch' then subtotal else 0 end) Hatch,
  sum(case when `grant`='NIH' then subtotal else 0 end) NIH,
  sum(case when `grant`='NSF' then subtotal else 0 end) NSF,
  sum(case when `grant`='Other' then subtotal else 0 end) Other,
  sum(case when `grant`='State' then subtotal else 0 end) State
from yourtable
group by month

See SQL Fiddle with Demo

Now, if you have an unknown number of values for Grant, then you can use a prepared statement to generate a dynamic version of this query:

SET @sql = NULL;
SELECT
  GROUP_CONCAT(DISTINCT
    CONCAT(
      'sum(case when `Grant` = ''',
      `Grant`,
      ''' then Subtotal else 0 end) AS `',
      `Grant`, '`'
    )
  ) INTO @sql
FROM yourtable;


SET @sql = CONCAT('SELECT month, ', @sql, ' 
                  FROM yourtable 
                  group by month');


PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

See SQL Fiddle with Demo

Both produce the same result:

|    MONTH |  HATCH |   NIH |    NSF |  OTHER |    DOE |  STATE |
-----------------------------------------------------------------
| Nov-2012 | 144.56 | 240.9 |  100.7 | 276.67 |      0 |      0 |
| Oct-2012 | 321.54 |     0 | 234.53 | 312.35 | 214.35 |      0 |
| Sep-2012 | 147.99 |     0 | 156.89 | 245.67 |      0 | 148.66 |
Taryn
  • 242,637
  • 56
  • 362
  • 405
  • bluefeet,Thanks so much for your effort. I ran your both codes in PHPMyAdmin. The first one gave me this: Month column: NULL. All other columns: 0.00. Your 2nd code returned only a message: Your SQL query has been executed successfully. I did not see a table data. – Jeff Lee Nov 12 '12 at 18:54
  • Okay. The problem could be the Month column. I had it as varchar, and you set it a date type. I will try again. – Jeff Lee Nov 12 '12 at 19:00
  • @JeffLee without seeing your full table structure it is difficult to say the problem. Are you including any other fields in your select? I am not able to replicate these issues see this demo -- http://sqlfiddle.com/#!2/7f11d/7 – Taryn Nov 12 '12 at 19:01
  • @JeffLee if it is a `varchar` datatype, then you can remove the date formatting. see my edit – Taryn Nov 12 '12 at 19:01
  • bluefeet, thanks again for the help. I created the same table as on your demo page. Then I ran the prepared statement code. Again the script was run successfully, but I did not see a table display. – Jeff Lee Nov 12 '12 at 19:44
  • This is the message: SET @sql = NULL ;# MySQL returned an empty result set (i.e. zero rows). SELECT GROUP_CONCAT( DISTINCT CONCAT( 'sum(case when `Grant` = ''', `Grant` , ''' then Subtotal else 0 end) AS `', `Grant` , '`' ) ) INTO @sql FROM yourtable;# 1 row(s) affected. SET @sql = CONCAT( 'SELECT month, ', @sql , ' FROM yourtable group by month' ) ;# MySQL returned an empty result set (i.e. zero rows). PREPARE stmt FROM @sql ;# MySQL returned an empty result set (i.e. zero rows). EXECUTE stmt;# Rows: 3 DEALLOCATE PREPARE stmt;# MySQL returned an empty result set (i.e. zero rows). – Jeff Lee Nov 12 '12 at 19:44
  • bluefeet, your first CASE statement code worked like a charm! – Jeff Lee Nov 12 '12 at 19:54
  • @JeffLee I don't have a PHPMyAdmin to test with at this moment. However, I know it does work. – Taryn Nov 12 '12 at 19:55
  • In PHPRunner, I got a syntax error on line 2 near "SELECT GROUP_CONTACT.".In visual MySQLBuilder, it first warned "Missing Select keyword". Then it could run through and gave expected result. In TeamQuery, it ran smoothly without any error. Any ideas? I wanted to use this script in PHPRunner... – Jeff Lee Nov 12 '12 at 21:45