问题
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.
回答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 |
来源:https://stackoverflow.com/questions/13348850/sql-query-to-rebuild-a-table-using-its-dynamic-row-data-for-column-names