Transpose row to columns in SQL/PHP - mysql

I fetched the following data:
MONTH | TOTAL
-------------------------
Jan | 100
Feb | 200
Mar | 300
Using this query:
$query = "SELECT DATE_FORMAT(date,'%b') AS MONTH, SUM(col1+col2) AS TOTAL FROM myTable GROUP BY YEAR(date),MONTH(date)";
How can I edit the above query or re-write to get the following result:
JAN | FEB | MAR
-------------------------
100 | 200 | 300
I have gone through almost all the the other similar posts. However, sql transposing, to me, is very confusing. Any input is much appreciated!

You can use conditional aggregation. The following will work in either SQL Server or MySQL:
select year(date),
sum(case when month(date) = 1 then col1 + col2 else 0 end) as jan,
sum(case when month(date) = 2 then col1 + col2 else 0 end) as feb,
sum(case when month(date) = 3 then col1 + col2 else 0 end) as mar
from mytable
group by year(date)
order by year(date);
EDIT (regarding the comment):
select year(date),
sum(case when month(date) = 1 then val else 0 end) as jan,
sum(case when month(date) = 2 then val else 0 end) as feb,
sum(case when month(date) = 3 then val else 0 end) as mar
from (select t.*, (col1 + col2) as val
from mytable
) t
group by year(date)
order by year(date);

Related

how to reuse the resultset in mysql query

I have result set like this after executing "select * from temp"
id | Month | value1 | value2 |
------------------------------
1 | Apr | 100 | 150
2 | May | 50 | 75
3 | Jan | 50 | 75
5 | Feb | 50 | 75
6 | mar | 50 | 75
I want to change it to column to rows like pivot table.
i used case when statement to change it.
select
'value1' as Field,
SUM(CASE
WHEN dc.month = 'January' THEN dc.value1
ELSE ''
END) AS January,
SUM(CASE
WHEN dc.month = 'February' THEN dc.value1
ELSE ''
END) AS February,
SUM(CASE
WHEN dc.month = 'March' THEN dc.value1
ELSE ''
END) AS March
SUM(CASE
WHEN dc.month = 'April' THEN dc.value1
ELSE ''
END) AS April,
SUM(CASE
WHEN dc.month = 'May' THEN dc.value1
ELSE ''
END) AS May
from
(select
* from
temp)dc
Field | Jan | Feb | Mar | Apr | May
-------------------------------------
Value1 | 50 | 50 | 50 | 100 | 50
I want to get same result set for value2 in same query without using UNION. Because temp table has lot of data's.
Field | Jan | Feb | Mar | Apr | May
-------------------------------------
Value1 | 50 | 50 | 50 | 100 | 50
Value1 | 75 | 75 | 75 | 150 | 75
This solution uses a union, but only to create "fake" rows to represent each value* column, and then joins and groups by those rows:
SELECT
`field`,
SUM(CASE
WHEN dc.month = 'Jan' THEN
CASE `field`
WHEN 'value1' THEN dc.value1
WHEN 'value2' THEN dc.value2
END
ELSE ''
END) AS January,
SUM(CASE
WHEN dc.month = 'Feb' THEN
CASE `field`
WHEN 'value1' THEN dc.value1
WHEN 'value2' THEN dc.value2
END
ELSE ''
END) AS February,
SUM(CASE
WHEN dc.month = 'Mar' THEN
CASE `field`
WHEN 'value1' THEN dc.value1
WHEN 'value2' THEN dc.value2
END
ELSE ''
END) AS March,
SUM(CASE
WHEN dc.month = 'Apr' THEN
CASE `field`
WHEN 'value1' THEN dc.value1
WHEN 'value2' THEN dc.value2
END
ELSE ''
END) AS April,
SUM(CASE
WHEN dc.month = 'May' THEN
CASE `field`
WHEN 'value1' THEN dc.value1
WHEN 'value2' THEN dc.value2
END
ELSE ''
END) AS May
FROM
(SELECT
* FROM temp
)dc
INNER JOIN (
SELECT 'value1' AS `field`
UNION SELECT 'value2'
) AS value_columns
GROUP BY `field`
If its just 2 colums i.e. value1 and value2 then you can use UNION as DEMO
select
'value1' as Field,
SUM(
CASE
WHEN dc.month = 'Jan' THEN dc.value1
ELSE ''
END) AS January,
SUM(
CASE
WHEN dc.month = 'Feb' THEN dc.value1
ELSE ''
END) AS February,
SUM(
CASE
WHEN dc.month = 'Mar' THEN dc.value1
ELSE ''
END) AS March,
SUM(
CASE
WHEN dc.month = 'Apr' THEN dc.value1
ELSE ''
END) AS April,
SUM(
CASE
WHEN dc.month = 'May' THEN dc.value1
ELSE ''
END)AS May
from
temp dc
UNION
select
'value2' as Field,
SUM(
CASE
WHEN dc.month = 'Jan' THEN dc.value2
ELSE ''
END) AS January,
SUM(
CASE
WHEN dc.month = 'Feb' THEN dc.value2
ELSE ''
END) AS February,
SUM(
CASE
WHEN dc.month = 'Mar' THEN dc.value2
ELSE ''
END) AS March,
SUM(
CASE
WHEN dc.month = 'Apr' THEN dc.value2
ELSE ''
END) AS April,
SUM(
CASE
WHEN dc.month = 'May' THEN dc.value2
ELSE ''
END)AS May
from temp dc

SUM DISTINCT MYSQL | WHERE CLAUSE

I would like to get results based on SUM from table (history), where username contains 'red' and grouped by month. here the query :
select month(date),
SUM(CASE WHEN status='success' THEN 1 ELSE 0 END) as total_sucess,
SUM(CASE WHEN status='failed' THEN 1 ELSE 0 END) as total_failed
from history
where date between '201305%' AND '201311%' AND username like '%#red%'
GROUP BY month(history.date);
the results :
+------------+--------------+--------------+
| month(date) | total_sucess | total_failed |
+------------+--------------+--------------+
| 5 | 10960 | 3573 |
| 6 | 2336 | 1202 |
| 7 | 2211 | 1830 |
| 8 | 5312 | 3125 |
| 9 | 9844 | 5407 |
| 10 | 6351 | 3972 |
+------------+--------------+--------------+
the question is , how do I get distinct total_success and total_failed SUM? just in one query ?
I've tried using this
select month(tgl),
SUM(CASE WHEN status='success' THEN 1 ELSE 0 END) as total_sucess,
SUM(DISTINCT (username) CASE WHEN status='success' THEN 1 ELSE 0 END) as distinct_total_sucess,
SUM(CASE WHEN status='failed' THEN 1 ELSE 0 END) as total_failed,
SUM(DISTINCT (username) CASE WHEN status='failed' THEN 1 ELSE 0 END) as distinct_failed_sucess
from history_auth
where tgl between '201305%' AND '201311%' AND username like '%#t.sel%'
GROUP BY month(history_auth.tgl);
but get error sql syntax... i have no idea with this :(
Best I can make out of your requirement is that you want the number of distinct usernames each month that succeeded / failed.
If so I think you need a pair of sub selects to get those figures.
Rejigged the query (adding another sub select to get the 6 months, rather than relying on all months being represented.
SELECT Sub1.aMonth,
SUM(CASE WHEN history.status='success' THEN 1 ELSE 0 END) as total_sucess,
SUM(CASE WHEN history.status='failed' THEN 1 ELSE 0 END) as total_failed,
IFNULL(SuccessCount, 0),
IFNULL(FailedCount, 0)
FROM
(
SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 0 MONTH)) AS aMonth
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 1 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 2 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 3 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 4 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 5 MONTH))
UNION SELECT MONTH(DATE_SUB('2013-11-01', INTERVAL 6 MONTH))
) Sub1
LEFT OUTER JOIN history
ON MONTH(history.date) = Sub1.aMonth
AND username LIKE '%#red%'
LEFT OUTER JOIN
(
SELECT MONTH(date) AS aMonth, COUNT(DISTINCT username) AS SuccessCount
FROM history
WHERE status='success'
AND username LIKE '%#red%'
GROUP BY MONTH(date)
) Sub2
ON Sub1.aMonth = Sub2.aMonth
LEFT OUTER JOIN
(
SELECT MONTH(date) AS aMonth, COUNT(DISTINCT username) AS FailedCount
FROM history
WHERE status='failed'
AND username LIKE '%#red%'
GROUP BY MONTH(date)
) Sub3
ON Sub1.aMonth = Sub3.aMonth
GROUP BY Sub1.aMonth, SuccessCount, FailedCount

MySQL - Combining multiple selects from same table into one result table with a group by

I have a table of meter readings which holds a meterNo, readingValue and readingDate.
I want to show the total sum of readingValue per meterNo per year.
For example:
SELECT meterNo, SUM(readingValue) '2009' FROM readings
WHERE readingDate >= '2009-01-01' AND readingDate <= '2009-12-31'
GROUP BY meterNo;
Will display the table:
meterNo 2009
--------------
4 50
5 5
12 30
13 63
18 18
26 484
27 21
28 510
29 28
Which is what I want, it shows the total reading value for each meter in 2009. However, I need the result table to display a column for each year. So it would be something like this:
meterNo 2009 2010 2011 2012
--------------------------------
1 20 35 50
2 45 35
3 50 60
4 50 35 20 30
5 5 10 10 15
6 30
And so on...
I can reuse the where part of the query:
WHERE readingDate >= '2009-01-01' AND readingDate <= '2009-12-31'
And just change the date for each year, butt how do I display each WHERE result as its own column in the one result table?
MySQL does not have a PIVOT function but you can convert the rows of data into columns using an aggregate function with a CASE expression.
If you have a limited number of years, then you can hard-code the query:
select meterNo,
sum(case when year(readingDate) = 2009 then readingValue else 0 end) `2009`,
sum(case when year(readingDate) = 2010 then readingValue else 0 end) `2010`,
sum(case when year(readingDate) = 2011 then readingValue else 0 end) `2011`,
sum(case when year(readingDate) = 2012 then readingValue else 0 end) `2012`,
sum(case when year(readingDate) = 2013 then readingValue else 0 end) `2013`
from readings
group by meterno;
See SQL Fiddle with Demo
But if you are going to have an unknown number of values or what the query to adjust as new years are added to the database, then you can use a prepared statement to generate dynamic SQL:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'sum(CASE WHEN year(readingDate) = ',
year(readingDate),
' THEN readingValue else 0 END) AS `',
year(readingDate), '`'
)
) INTO #sql
FROM readings;
SET #sql
= CONCAT('SELECT meterno, ', #sql, '
from readings
group by meterno');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo. Both give the result:
| METERNO | 2009 | 2010 | 2012 | 2013 | 2011 |
----------------------------------------------
| 1 | 90 | 180 | 0 | 90 | 90 |
| 2 | 50 | 0 | 90 | 0 | 0 |
| 3 | 80 | 40 | 90 | 90 | 0 |
As a side note, if you want null to display in the rows without values instead of the zeros, then you can remove the else 0 (see Demo)
You can use CASE WHEN to do this. It allows you to sum multiple values based on multiple conditions. In your example, you could do something like this (I've simplified my example because I don't have access to your actual data):
SELECT meterNo,
sum(CASE WHEN readingDate = 2009 THEN readingValue END) as '2009',
sum(CASE WHEN readingDate = 2010 THEN readingValue END) as '2010',
sum(CASE WHEN readingDate = 2011 THEN readingValue END) as '2011',
sum(CASE WHEN readingDate = 2012 THEN readingValue END) as '2012'
FROM readings
GROUP BY meterNo
You need to switch up the conditions with your own--sum(CASE WHEN readingDate >= '2009-01-01' AND readingDate <= '2009-12-31' THEN readingValue END), etc--but it should work. Here's a working example that I did on sqlFiddle: http://sqlfiddle.com/#!2/6990e/29. Output is five columns with sums of each meterNo.
You could do something like this:
SELECT
meterNo, SUM(readingValue) AS `total`, YEAR(readingDate)
FROM
readings
GROUP BY
meterNo, YEAR(readingDate);
result:
METERNO TOTAL YEAR(READINGDATE)
1 90 2009
1 90 2010
2 50 2009
3 80 2009
3 40 2010
Also note how you can use YEAR function instead of the where clause you have.
SEE FIDDLE

how i can get these with mysql?

I am new to mysql.
I have on survey with clicks, period(date). Now i have to find out number of clicks per month, like:
MON CLICKS
nov 0
oct 34
sep 67
aug 89
I have used code like this:
select MONTHNAME(period) mon, IFNULL(count(id),0) as Clicks
from survey
where period > DATE_SUB(now(), INTERVAL 3 MONTH)
group by EXTRACT(MONTH FROM period)
It is not working for with no records.
Here one thing I suppose there is no record in that month it should show 0: if there is no record in nov the number of clicks should be 0.
my table structure was like this
CREATE TABLE `survey` (
`id` int(2) NOT NULL auto_increment,
`period` datetime default NULL)
for last four weeks i have used
SELECT uq.timespan, COALESCE(tsq.TotalClicks, 0) as Clicks FROM (
SELECT '22-28 days' as timespan
union SELECT '15-21 days'
union SELECT '8-14 days'
union SELECT 'up to 7 days'
)uq LEFT JOIN (
SELECT CASE
WHEN submitdate >= NOW() - INTERVAL 4 WEEK
AND submitdate < NOW() - INTERVAL 3 WEEK THEN '22-28 days'
WHEN submitdate >= NOW() - INTERVAL 3 WEEK
AND submitdate < NOW() - INTERVAL 2 WEEK THEN '15-21 days'
WHEN submitdate >= NOW() - INTERVAL 2 WEEK
AND submitdate < NOW() - INTERVAL 1 WEEK THEN '8-14 days'
WHEN submitdate >= NOW() - INTERVAL 1 WEEK THEN 'up to 7 days'
END Weeksubmitdate,
count(id) TotalClicks
FROM survey
WHERE submitdate >= NOW() - INTERVAL 4 WEEK
GROUP BY Weeksubmitdate
)tsq ON uq.timespan = tsq.Weeksubmitdate
Any help?
I usually do Pivot table to achieve this. Assuming your click information is stored into a table named SURVEY and assuming only the date/time of the click is stored into one column of the SURVEY table (which is all what you need) then here is one way to do it:
select year(period),
sum(case when month(period)=1 then 1 else 0 end) jan,
sum(case when month(period)=2 then 1 else 0 end) feb,
sum(case when month(period)=3 then 1 else 0 end) mar,
sum(case when month(period)=4 then 1 else 0 end) apr,
sum(case when month(period)=5 then 1 else 0 end) may,
sum(case when month(period)=6 then 1 else 0 end) jun,
sum(case when month(period)=7 then 1 else 0 end) jul,
sum(case when month(period)=8 then 1 else 0 end) aug,
sum(case when month(period)=9 then 1 else 0 end) sep,
sum(case when month(period)=10 then 1 else 0 end) oct,
sum(case when month(period)=11 then 1 else 0 end) nov,
sum(case when month(period)=11 then 1 else 0 end) dec
from survey
group by year(period)
The output is something like:
---------------------------------------------------------------------------------
| Year | JAN | FEB | MAR | APR | MAY | JUN | JUL | AUG | SEP | OCT | NOV | DEC |
---------------------------------------------------------------------------------
| 2012 | 5 | 20 | 13 | 0 | 0 | 65 | 15 | 0 | 0 | 21 | 0 | 0 |
---------------------------------------------------------------------------------
I even set up the same Fiddle SQL for you
SQL Fiddle Demo
An alternative way (Column based for the last 4 months even with ZERO count):
SQL Fiddle Demo
SELECT mon,
sum(clicks) clicks
FROM ( SELECT month(period) mnth,
date_format(period,'%b') mon,
count(1) clicks
FROM survey
WHERE month(period) BETWEEN month(curdate()) - 4 AND month(curdate())
GROUP BY 1, 2
UNION ALL
SELECT 1 mnth, 'Jan' mon, 0 clicks
UNION ALL
SELECT 2 mnth, 'Feb' mon, 0 clicks
UNION ALL
SELECT 3 mnth, 'Mar' mon, 0 clicks
UNION ALL
SELECT 4 mnth, 'Apr' mon, 0 clicks
UNION ALL
SELECT 5 mnth, 'May' mon, 0 clicks
UNION ALL
SELECT 6 mnth, 'Jun' mon, 0 clicks
UNION ALL
SELECT 7 mnth, 'Jul' mon, 0 clicks
UNION ALL
SELECT 8 mnth, 'Aug' mon, 0 clicks
UNION ALL
SELECT 9 mnth, 'Sep' mon, 0 clicks
UNION ALL
SELECT 10 mnth, 'Oct' mon, 0 clicks
UNION ALL
SELECT 11 mnth, 'Nov' mon, 0 clicks
UNION ALL
SELECT 12 mnth, 'Dec' mon, 0 clicks) a
WHERE mnth BETWEEN month(curdate()) - 4 AND month(curdate())
GROUP BY 1
ORDER BY mnth
You need to join to a table that contains all month names. Here's one way to do it:
select
mon,
ifnull(count(id), 0) as Clicks
from (select 'nov' as mon union select 'oct' union select 'sep' union select 'aug') m
left join survey on MONTHNAME(period) = mon
where submitdate > DATE_SUB(now(), INTERVAL 3 MONTH)
group by 1
select MONTHNAME(STR_TO_DATE(month(period), '%m'))as 'month',count(*) as clicks
from survey group by month(period)

How can I GROUP BY more than one row/column?

I'm looking for a way to group more than one column in mysql.
The table is pretty much:
Customer, Product, Date
Joe, Apple, 2011-01-01
Henry, Banana, 2011-05-26
Sally, Peach, 2011-06-02
Jane, Strawberry, 2010-06-25
What I wanna do is count the NUMBER of Produkts each customer bought and group that by the month of the year.
So it would look like.
COUNT(Product) | January | February | March ....... | Total
If I just use GROUP BY product, MONTH(date) I get
Banana, January, 2
Banana, February, 3
Banana, March, 1
and so on.
Is there a way to do this, so that I get as many rows as I have distinct products and then a column for each month?
I'm really trying NOT to do this lateron in PHP because it get's incredibly slow.
Thanks a lot, already!
SELECT Product,
SUM(CASE WHEN MONTH(date) = 1 THEN 1 ELSE 0 END) Jan,
SUM(CASE WHEN MONTH(date) = 2 THEN 1 ELSE 0 END) Feb,
SUM(CASE WHEN MONTH(date) = 3 THEN 1 ELSE 0 END) Mar,
SUM(CASE WHEN MONTH(date) = 4 THEN 1 ELSE 0 END) Apr,
SUM(CASE WHEN MONTH(date) = 5 THEN 1 ELSE 0 END) May,
SUM(CASE WHEN MONTH(date) = 6 THEN 1 ELSE 0 END) Jun,
SUM(CASE WHEN MONTH(date) = 7 THEN 1 ELSE 0 END) Jul,
SUM(CASE WHEN MONTH(date) = 8 THEN 1 ELSE 0 END) Aug,
SUM(CASE WHEN MONTH(date) = 9 THEN 1 ELSE 0 END) Sep,
SUM(CASE WHEN MONTH(date) = 10 THEN 1 ELSE 0 END) Oct,
SUM(CASE WHEN MONTH(date) = 11 THEN 1 ELSE 0 END) Nov,
SUM(CASE WHEN MONTH(date) = 12 THEN 1 ELSE 0 END) Dec,
COUNT(date) Total
FROM
MyTable
GROUP BY
product