mysql table data rows to column - mysql

I'm trying to achieve yearly data as pivot, this is my query
SELECT
month(te.topup_date) as month,
sum(CASE WHEN year(te.topup_date) = '2015' THEN te.topup_amount+ tp.topup_amount END) as '2015',
sum(CASE WHEN year(te.topup_date) = '2016' THEN te.topup_amount+ tp.topup_amount END) as '2016'
from te_daily_topup as te
inner join tp_daily_topup as tp on year(te.topup_date) = year(tp.topup_date)
where year(te.topup_date) between '2015' and '2016'
group by year(te.topup_date), month(te.topup_date)
The result from the above query
month | 2015 | 2016
--------------------
1 | 123 |
2 | 2343 |
1 | |234
2 | |7667
The result I'm looking for is below:
month | 2015 | 2016
--------------------
1 | 123 | 234
2 | 2343 | 7667
help me to modify this query..

Just remove year() from the GROUP BY:
select month(te.topup_date) as month,
sum(case when year(te.topup_date) = 2015 then te.topup_amount + tp.topup_amount end) as `2015`,
sum(case when year(te.topup_date) = 2016 then te.topup_amount + tp.topup_amount end) as `2016`
from te_daily_topup te inner join
tp_daily_topup tp
on year(te.topup_date) = year(tp.topup_date)
where year(te.topup_date) between 2015 and 2016
group by month(te.topup_date);
Note: Do not use single quotes for integer constants nor for column aliases. Only use single quotes for string and date constants.

Related

My SUM query returns 2 rows, need some advise

I am getting 2 rows(9500) for 111 in the result, could you please advise on the right approach, I need like, Balance = (sum of bought - sum of sold)
#Table T1#
+---------+--------+------+------+
| ACCOUNT | TRANS | AMT | YEAR |
+---------+--------+------+------+
| 111 | BOUGHT | 8000 | 2019 |
| 111 | BOUGHT | 2000 | 2019 |
| 111 | SOLD | 500 | 2019 |
| 222 | BOUGHT | 6000 | 2018 |
| 222 | SOLD | 300 | 2018 |
+---------+--------+------+------+
Query
SELECT (A.BOUGHTs - B.SOLDs) AS BALANCE
FROM T1
INNER JOIN
(SELECT SUM(AMT) AS BOUGHTs
FROM T1
WHERE TRANS = 'BOUGHT'
) A
ON T1.ACCOUNT = A.ACCOUNT
INNER JOIN
(SELECT SUM(AMT) AS SOLDs
FROM T2
WHERE TRANS = 'SOLD'
) B
ON T1.ACCOUNT = B.ACCOUNT
WHERE T1.ACCOUNT = 111
AND T1.YEAR = 2019
You can use conditional aggregation:
select account, year,
sum(case when trans = 'bought' then amt
when trans = 'sold' then - amt
else 0
end) as diff
from t
group by account, year;
If you want this for only one account, you can add a where clause:
select account, year,
sum(case when trans = 'bought' then amt
when trans = 'sold' then - amt
else 0
end) as diff
from t
where account = 111 and year = 2019
group by account, year;
Rewriting your query will look like this:
SELECT (A.BOUGHTs - B.SOLDs) AS BALANCE
FROM
(SELECT ACCOUNT,YEAR,SUM(AMT) AS BOUGHTs FROM T1 WHERE TRANS = 'BOUGHT' GROUP BY ACCOUNT,YEAR) A
INNER JOIN
(SELECT ACCOUNT,YEAR,SUM(AMT) AS SOLDs FROM T1 WHERE TRANS = 'SOLD' GROUP BY ACCOUNT,YEAR) B
ON A.ACCOUNT=B.ACCOUNT AND A.YEAR=B.YEAR
WHERE A.ACCOUNT = 111
AND A.YEAR = 2019;

MySQL : weekly and monthly average

This is my bill table:
shop_id | billing_date | total
------------------------------
ABC | 2016-03-07 | 100
ABC | 2016-03-14 | 200
DEF | 2016-03-07 | 300
DEF | 2016-03-14 | 100
GHI | 2016-03-07 | 30
I want to get one line per shop, with average total per week, the current month total, and the average total per month. This final data must look like this:
shop | weekly avg. | current month total | monthly avg.
-------------------------------------------------------
ABC | 150 | 300 | 300
DEF | 200 | 500 | 500
GHI | 30 | 30 | 30
My question is: Is it possible to get this informations directly from an SQL query?
Hey you can try this way for current year using WEEK and MONTH of mysql. as per your data entries in table is week wise:
SQLFIDDLE
select shop_id,(sum(total)/(WEEK(MAX(bdate)) - WEEK(MIN(bdate))+1)) as weekly_avg,(sum(total)/(MONTH(MAX(bdate))-MONTH(MIN(bdate))+1)) as mothly_avg, sum( case when MONTH(bdate) = MONTH(NOW()) then total else 0 end) as current_month_total from bill group by shop_id WHERE YEAR(bdate) = 2016
For number of year greater than one
SQL FIDDLE
select shop_id,
sum(total)/(12 * (YEAR(MAX(bdate)) - YEAR(MIN(bdate))) + (MONTH(MAX(bdate)) - MONTH(MIN(bdate)))+1) as month_avg,
sum(total)/(7 * (YEAR(MAX(bdate)) - YEAR(MIN(bdate))) + (WEEK(MAX(bdate)) - WEEK(MIN(bdate)))+1) as weekly_avg,
sum( case when YEAR(bdate) = YEAR(bdate) and MONTH(bdate) = MONTH(NOW()) then total else 0 end) as current_month_total from bill group by shop_id
Is this the sort of thing you are after??:
SELECT DISTINCT(bill.shop_id),wk as WeeklyTotal,mt as MonthlyTotal,ma as MonthlyAverage
FROM bill
JOIN (SELECT AVG(total) wk,shop_id
FROM bill
WHERE YEAR(billing_date) = 2016 AND MONTH(billing_date) = 1
GROUP BY shop_id) as weekly ON bill.shop_id = weekly.shop_id
JOIN (SELECT SUM(total) mt,shop_id
FROM bill
WHERE YEAR(billing_date) = 2016 AND MONTH(billing_date) = 1
GROUP BY CONCAT(shop_id,MONTH(billing_date))
) month_total ON month_total.shop_id = bill.shop_id
JOIN (SELECT AVG(total) ma,shop_id
FROM bill
WHERE YEAR(billing_date) = 2016 AND MONTH(billing_date) = 1
GROUP BY CONCAT(shop_id,MONTH(billing_date))
) month_avg ON month_avg.shop_id = bill.shop_id
You can do this using conditional aggregation and conditional logic:
select shop_id,
sum(total) / (7 * datediff(max(billing_date), min(billing_date)) + 1) as avg_weekly,
sum(case when year(billing_date) = year(now()) and month(billing_date) = month(now()) then total else 0 end) as curr_Month,
(sum(total) /
(year(max(billing_date)) * 12 + month(max(billing_date)) -
year(min(billing_date)) * 12 + month(min(billing_date))
) + 1
)
) as avg_month
total else 0 end) as week_total
from bill
gropu by shop_id;

Create a Summary View in MySQL by pivoting row into dynamic number of columns

I have a table in MySQL with the following fields:
id, company_name, year, state
There are multiple rows for the same customer and year, here is an example of the data:
id | company_name | year | state
----------------------------------------
1 | companyA | 2008 | 1
2 | companyB | 2009 | 2
3 | companyC | 2010 | 3
4 | companyB | 2009 | 1
5 | companyC | NULL | 3
I am trying to create a view from this table to show one company per row (i.e. GROUP BY pubco_name) where the state is the highest for a given year.
Here is an example of the view I am trying to create:
id | cuompany_name | NULL | 2008 | 2009 | 2010
--------------------------------------------------
1 | companyA | NULL | 1 | NULL | NULL
2 | companyB | NULL | 2 | NULL | NULL
3 | companyC | 3 | NULL | NULL | 3
There is a lot more data than this, but you can see what I am trying to accomplish.
I don't know how to select the max state for each year and group by pubco_name.
Here is the SQL I have thus far (I think we need to use CASE and/or sub-selects here):
SELECT
id,
company_name,
SUM(CASE WHEN year = 2008 THEN max(state) ELSE 0 END) AS 2008,
SUM(CASE WHEN year = 2009 THEN max(state) ELSE 0 END) AS 2009,
SUM(CASE WHEN year = 2010 THEN max(state) ELSE 0 END) AS 2010,
SUM(CASE WHEN year = 2011 THEN max(state) ELSE 0 END) AS 2011,
SUM(CASE WHEN year = 2012 THEN max(state) ELSE 0 END) AS 2012,
SUM(CASE WHEN year = 2013 THEN max(state) ELSE 0 END) AS 2013
FROM tbl
GROUP BY company_name
ORDER BY id DESC
Appreciate your help and thanks in advance.
You need to pivot the table but mysql does not have any such functionality of pivot
so we need to replicate its functionality
EDITED
Select
group_concat(
DISTINCT
if(year is null,
CONCAT('max(if (year is null, state, 0)) as ''NULL'' '),
CONCAT('max(if (year=''', year, ''', state, 0)) as ''',year, ''' '))
) into #sql from tbl join (SELECT #sql:='')a;
set #sql = concat('select company_name, ', #sql, 'from tbl group by company_name;');
PREPARE stmt FROM #sql;
EXECUTE stmt;
Result
| COMPANY_NAME | 2008 | 2009 | 2010 | NULL |
--------------------------------------------
| companyA | 1 | 0 | 0 | 0 |
| companyB | 0 | 2 | 0 | 0 |
| companyC | 0 | 0 | 3 | 3 |
SQL FIDDLE
There are 2 approaches to solve your problem
1. create case for each year, which is not possible in your case as we are dealing with year
2. generate the query dynamically so that we get proper columns as per your need.
I have given solution according to the second solution where I am generating the query and storing it in #sql variable. In the fiddle I have printed the contents of #sql before executing it.
select company_name, max(if (year='2008', state, 0)) as '2008' ,max(if (year='2009', state, 0)) as '2009' ,max(if (year='2010', state, 0)) as '2010' ,max(if (year is null, state, 0)) as 'NULL' from tbl group by company_name;
For more information regarding group_concat() go through the link
GROUP_CONCAT and
USER DEFINED VARIABLE
Hope this helps..
Please see the page linked in the answer to this question.
Note that when you do this, you must specify ahead of time how many columns you want in your output.
In response to the comment below, here is a simple/ basic implementation that reproduces the result table above (except for the ID column; having it makes no sense, as each row in the result can summarize more than one row in the input table)
SELECT
`company_name`,
NULLIF(SUM(CASE WHEN `t3`.`year` IS NULL THEN `t3`.`state` ELSE 0 END), 0) AS `null`,
NULLIF(SUM(CASE WHEN `t3`.`year` = 2008 THEN `t3`.`state` ELSE 0 END), 0) AS `2008`,
NULLIF(SUM(CASE WHEN `t3`.`year` = 2009 THEN `t3`.`state` ELSE 0 END), 0) AS `2009`,
NULLIF(SUM(CASE WHEN `t3`.`year` = 2010 THEN `t3`.`state` ELSE 0 END), 0) AS `2010`
FROM
(
SELECT
`t1`.`id`,
`t1`.`company_name`,
`t1`.`year`,
`t1`.`state`
FROM `tbl` `t1`
WHERE `t1`.`state` = (
SELECT MAX(`state`)
FROM `tbl` `t2`
WHERE `t2`.`company_name` = `t1`.`company_name`
AND (`t2`.`year` IS NULL AND `t1`.`year` IS NULL OR `t2`.`year` = `t1`.`year`)
)
) `t3`
GROUP BY `t3`.`company_name`;
This uses nested queries: the inner ones (with the t1 and t2 aliases) find the row with the maximum state for each year and company (and which will break unless you can be sure that this is unique!), and the outer one t3 does the pivot.
I would test this thoroughly to ensure performance is acceptable on real data.

Weekly report behalf of specific date field

I am using this query for weekly reporting but can not found a way like this
week_number | week_startdate | organization_1 | organization_2
---------------------------------------------------------------
1 | 2013-01--05 |count(date) like 4,24,etc_ | count(date) like 4,24,etc_
SQL:
SELECT WEEK(signed_date) AS week_name, signed_date AS Week_Starting,
YEAR(signed_date), WEEK(signed_date), COUNT(*)
FROM business
WHERE YEAR(signed_date) = YEAR(CURDATE())
GROUP BY CONCAT(YEAR(signed_date), '/', WEEK(signed_date))
ORDER BY YEAR(signed_date), WEEK(signed_date
SAMPLE DATA:
signed_date | organization_id
01-01-2013 | 1
02-01-2013 | 1
03-01-2013 | 2
In 1 week organization_1 have 2 signed & organization_2 has 1 signed.
You should use case within count or sum:
SELECT WEEK(signed_date) AS week_name, signed_date AS Week_Starting,
YEAR(signed_date), WEEK(signed_date),
SUM(CASE WHEN organization_id=1 THEN 1 ELSE 0 END) as organization_1,
SUM(CASE WHEN organization_id=2 THEN 1 ELSE 0 END) as organization_2
FROM business
WHERE YEAR(signed_date) = YEAR(CURDATE())
GROUP BY CONCAT(YEAR(signed_date), '/', WEEK(signed_date))
ORDER BY YEAR(signed_date), WEEK(signed_date);
http://sqlfiddle.com/#!2/587ad/3

MySQL, how to do a sum by product and by month

I have a table like below
Date | Product | Qty
-------------|---------------|------
12-Dec-12 | reference1 | 1
14-Dec-12 | reference2 | 2
14-Dec-12 | reference1 | 3
1-Jan-13 | reference2 | 4
3-Jan-13 | reference2 | 5
3-Jan-13 | reference3 | 6
and I would like to get it as below through a query
Product | Dec 2012 | Jan 2013
===========|============|==========
reference1 | 4 | 0
reference2 | 2 | 9
reference3 | 0 | 6
I know how to group already, my problem is how to have dynamic column (I would like to be able to choose last 6 months, 12 months or 24 months).
You are attempting to pivot the data from rows into columns. MySQL does not have a pivot function but you can use an aggregate function with a CASE to get the result:
select product,
sum(case when month(date) = 12 and year(date) = 2012
then qty else 0 end) Dec2012,
sum(case when month(date) = 1 and year(date) = 2013
then qty else 0 end) Jan2013
from yourtable
group by product
See SQL Fiddle with Demo.
This can also be written using a subquery to get the date in the format of Month-year:
select product,
sum(case when MonthYear = 'Dec_2012' then qty else 0 end) Dec2012,
sum(case when MonthYear = 'Jan_2013' then qty else 0 end) Jan2013
from
(
select product,
date_format(date, '%b_%Y') MonthYear,
qty
from yourtable
) src
group by product;
See SQL Fiddle with Demo.
Then if you wanted to generate a list of dates dynamically or will have an unknown number of dates that you want to return, you can use a prepared statement to generate dynamic SQL:
SET #sql = NULL;
SELECT
GROUP_CONCAT(DISTINCT
CONCAT(
'sum(case when MonthYear = ''',
MonthYear,
''' then qty else 0 end) AS ',
MonthYear
)
) INTO #sql
FROM
(
select product,
date_format(date, '%b_%Y') MonthYear,
qty
from yourtable
) src;
SET #sql = CONCAT('SELECT product, ', #sql, '
from
(
select product,
date_format(date, ''%b_%Y'') MonthYear,
qty
from yourtable
) src
GROUP BY product');
PREPARE stmt FROM #sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;
See SQL Fiddle with Demo. All three will give you the result:
| PRODUCT | DEC_2012 | JAN_2013 |
------------------------------------
| reference1 | 4 | 0 |
| reference2 | 2 | 9 |
| reference3 | 0 | 6 |