making rows to columns in mysql - mysql

i have this hour wise data , i want to generate reports where i have to show the revenue every hour in a single row groupy by product,mode,region (sorry for bad format)
price product mode region HOUR
1. 0 p1 direct reg1 1
2. 10 p3 indirect reg2 2
3. 0 p2 direct reg1 1
4. 0 p1 indirect reg2 2
5. 0 p2 direct reg5 3
6. 0 p1 direct reg1 3
7. 0 p1 direct reg2 3
8. 0 p3 indirect reg4 17
9. 0 p4 direct reg2 17
10. 21 p1 direct reg2 17
HOUR1 HOUR2 HOUR3 HOUR4 HOUR5....HOUR23 product mode region
1. 0 10 1 30 p1 direct reg1
2. 0 5 1 10 p1 indirect reg2

Unfortunately mysql does not have any inbuilt pivot function so you need to either write a dynamic sql for the unknown set of pivot element or can use the most commonly used pivot generating technique for known set of pivot element, and in your case its known set since you are looking at pivot data from hour1 till hour23 and it could be done as
select
sum( case when HOUR = 1 then price else 0 end) as `HOUR1`,
sum( case when HOUR = 2 then price else 0 end) as `HOUR2`,
sum( case when HOUR = 3 then price else 0 end) as `HOUR3`,
sum( case when HOUR = 4 then price else 0 end) as `HOUR4`,
sum( case when HOUR = 5 then price else 0 end) as `HOUR5`,
sum( case when HOUR = 6 then price else 0 end) as `HOUR6`,
sum( case when HOUR = 7 then price else 0 end) as `HOUR7`,
sum( case when HOUR = 8 then price else 0 end) as `HOUR8`,
sum( case when HOUR = 9 then price else 0 end) as `HOUR9`,
sum( case when HOUR = 10 then price else 0 end) as `HOUR10`,
sum( case when HOUR = 11 then price else 0 end) as `HOUR11`,
sum( case when HOUR = 12 then price else 0 end) as `HOUR12`,
sum( case when HOUR = 13 then price else 0 end) as `HOUR13`,
sum( case when HOUR = 14 then price else 0 end) as `HOUR14`,
sum( case when HOUR = 15 then price else 0 end) as `HOUR15`,
sum( case when HOUR = 16 then price else 0 end) as `HOUR16`,
sum( case when HOUR = 17 then price else 0 end) as `HOUR17`,
sum( case when HOUR = 18 then price else 0 end) as `HOUR18`,
sum( case when HOUR = 19 then price else 0 end) as `HOUR19`,
sum( case when HOUR = 20 then price else 0 end) as `HOUR20`,
sum( case when HOUR = 21 then price else 0 end) as `HOUR21`,
sum( case when HOUR = 22 then price else 0 end) as `HOUR22`,
sum( case when HOUR = 23 then price else 0 end) as `HOUR23`,
product,
mode,
region
from mytable
group by product,mode,region;
http://sqlfiddle.com/#!9/b2e868/1

Related

SQL how to aggregate by date and multiple criteria

I have this table:
id
amount
method
date
01
10
A
2022-01-24 12:27:14.440
02
80
A
2022-01-24 12:27:14.440
01
20
D
2022-02-24 12:27:14.440
01
10
D
2022-02-24 12:27:14.440
02
20
D
2022-02-24 12:27:14.440
03
30
D
2022-02-24 12:27:14.440
and I want this:
method
amount_sum_jan
n_transaction_jan
n_customer_jan
amount_sum_feb
n_transaction_feb
n_customer_feb
A
10
2
2
0
0
0
D
0
0
0
80
4
3
This is a table with 7 column and rows equal to the number of methods.
AMOUNT: sum of amount in one month of one method
N_TRANSACTIONS: number of transaction in one month with one method
N_CUSTOMER: number of customers (id) who used that method in one month
Can I get it with just one query?
You want to aggregate your data by method and have separate columns for January data and February data. You get this with conditional aggregation (CASE expression inside the aggregate function),
select
method,
sum(case when month(date) = 1 then amount else 0 end) as amount_sum_jan,
count(case when month(date) = 1 then 1 end) as n_transaction_jan,
count(distinct case when month(date) = 1 then id end) as n_customer_jan,
sum(case when month(date) = 2 then amount else 0 end) as amount_sum_feb,
count(case when month(date) = 2 then 1 end) as n_transaction_feb,
count(distinct case when month(date) = 2 then id end) as n_customer_feb
from mytable
group by method
order by method;
It is called pivot, and would for fix dates look like this.
An aggregation of the method for the year and month, and you can COUNT or SUM your number
select
`method`,
SUM(CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202201' then `amount` ELSE 0 END) amount_sum_jan,
SUM(CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202201' then 1 ELSE 0 END) n_transaction_jan,
COUNT(DISTINCT CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202201' then `d` ELSE 0 END) n_customer_jan,
SUM(CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202202' then `amount` ELSe 0 END) amount_sum_feb,
SUM(CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202202' then 1 ELSe 0 END) n_transaction_feb,
COUNT(DISTINCT CASE WHEN EXTRACT( YEAR_MONTH FROM `date` ) = '202202' then `d` ELSe 0 END) n_customer_feb
from tab1
GROUP BY `method`
http://www.sqlfiddle.com/#!9/31d8ef/10
much more interesting would be to make that dynamic

Query with multiple AND conditions

I have my query like below
SELECT dates,
COUNT(link_data_id) as TotalClicks,
sum(case when link_redirect_status = 1 then 1 else 0 end) AS GoodClicks,
sum(case when link_redirect_status != 1 then 1 else 0 end) AS BadClicks
FROM tbl_calendar
LEFT JOIN tbl_links_data ON dates = CAST(link_data_time AS DATE)
WHERE (`dates` BETWEEN '2021-11-28' AND DATE_ADD('2021-11-28', INTERVAL 7 DAY))
GROUP BY dates
It's giving me expected output like below
But I want add one more condition called link_order_id ='abcde', so I am trying like below
SELECT dates,
COUNT(link_data_id) as TotalClicks,
sum(case when link_redirect_status = 1 then 1 else 0 end) AS GoodClicks,
sum(case when link_redirect_status != 1 then 1 else 0 end) AS BadClicks
FROM tbl_calendar
LEFT JOIN tbl_links_data ON dates = CAST(link_data_time AS DATE)
WHERE link_order_id = 'abcde'
AND (`dates` BETWEEN '2021-11-28' AND DATE_ADD('2021-11-28', INTERVAL 7 DAY))
GROUP BY dates
But it's giving me only two rows like below
Why I am getting only two rows instead of 8 rows like first picture?
Move the criteria in the WHERE clause to the ON clause of the join:
SELECT
c.dates,
COUNT(d.link_data_id) AS TotalClicks,
SUM(CASE WHEN d.link_redirect_status = 1 THEN 1 ELSE 0 END) AS GoodClicks,
SUM(CASE WHEN d.link_redirect_status != 1 THEN 1 ELSE 0 END) AS BadClicks
FROM tbl_calendar c
LEFT JOIN tbl_links_data d
ON c.dates = CAST(d.link_data_time AS DATE) AND
d.link_order_id = 'abcde'
WHERE (c.dates BETWEEN '2021-11-28' AND DATE_ADD('2021-11-28', INTERVAL 7 DAY))
GROUP BY c.dates;

SQL display age range

I'm writing a query for age range, in which I want to show the count of people of all age ranges eg
AGE PEOPLE
"0-10" 0
"11-20" 2
"21-30" 5
"31-40" 0
"41-50" 1
I've tried using
SELECT SUM(CASE WHEN age < 10 THEN 1 ELSE 0 END) AS [Under 10],
SUM(CASE WHEN age BETWEEN 11 AND 20 THEN 1 ELSE 0 END) AS [11-20],
SUM(CASE WHEN age BETWEEN 21 AND 30 THEN 1 ELSE 0 END) AS [21-30]
FROM people
But it shows ranges as column names
0-10 11-20 21-30 31-40 41-50
0 2 5 0 1
which i dont want.
I have also tried GROUP BY but it didn't show the ranges in which the count was 0.
You can use UNION ALL:
SELECT '[Under 10]' as Age, SUM(CASE WHEN age < 10 THEN 1 ELSE 0 END) as People
FROM people
UNION ALL
SELECT '[11-20]', SUM(CASE WHEN age BETWEEN 11 AND 20 THEN 1 ELSE 0 END)
FROM people
UNION ALL
SELECT '[21-30]', SUM(CASE WHEN age BETWEEN 21 AND 30 THEN 1 ELSE 0 END)
FROM people;
you case when should be like below
CASE WHEN age < 10 then '0-10'
when age age BETWEEN 11 AND 20 then '11-20'
when age BETWEEN 21 AND 30 then '21-30'
..... end as agegroup,--put here more according to your need
count(*)
from table group by agegroup
You need to perform UNION All for this.
SELECT SUM(CASE WHEN age < 10 THEN 1 ELSE 0 END) AS PEOPLE, 'UNDER 10' AS AGE FROM people
UNION ALL
SELECT SUM(CASE WHEN age BETWEEN 11 AND 20 THEN 1 ELSE 0 END) AS PEOPLE, `11-20` FROM people
UNION ALL
SELECT SUM(CASE WHEN age BETWEEN 21 AND 30 THEN 1 ELSE 0 END) , `21-30` FROM people
You want to get the group of result in rows so need to perform UNION in this case.
Please find this link for more info on UNION in MYSQL.link
If you are going to use UNION, use UNION ALL and move the conditions to the WHERE clause:
SELECT '[Under 10]' as Age, COUNT(*)
FROM people
WHERE age < 10
UNION
SELECT '[11-20]', COUNT(*)
FROM people
WHERE BETWEEN 11 AND 20
UNION ALL
SELECT '[21-30]', COUNT(*)
FROM people
WHERE age BETWEEN 21 AND 30;
Filtering and UNION ALL both improve performance. (UNION incurs overhead for removing duplicates).
There are other approaches. For instance, you can unpivot your table:
SELECT grp.age,
(CASE grp
WHEN 1 THEN [Under 10]
WHEN 2 THEN [11-20]
WHEN 3 THEN [21-30]
END)
FROM (SELECT SUM(CASE WHEN age < 10 THEN 1 ELSE 0 END) AS [Under 10]
SUM(CASE WHEN age BETWEEN 11 AND 20 THEN 1 ELSE 0 END) AS [11-20],
SUM(CASE WHEN age BETWEEN 21 AND 30 THEN 1 ELSE 0 END) AS [21-30]
FROM people p
) p CROSS JOIN
(SELECT 1 as grp, '[Under 10]' as age UNION ALL
SELECT 2 as grp, '[11-20]' as age UNION ALL
SELECT 3, as grp, '[21-30]' as age
) grps;
Although this looks more complicated, it is much better from a performance perspective, because it only scans the original table once.
There are other variants as well that only touch the original table once.

Calculate percentage and total after create categories mysql

I've this query
SELECT
trage,
CASE trage
WHEN '<18' THEN SUM(CASE WHEN AGE <18 THEN 1 ELSE 0 END)
WHEN '18-24' THEN SUM(CASE WHEN AGE >= 18 AND AGE <= 24 THEN 1 ELSE 0 END)
WHEN '25-34' THEN SUM(CASE WHEN AGE >= 25 AND AGE <= 34 THEN 1 ELSE 0 END)
WHEN '35-44' THEN SUM(CASE WHEN AGE >= 35 AND AGE <= 44 THEN 1 ELSE 0 END)
WHEN '45-54' THEN SUM(CASE WHEN AGE >= 45 AND AGE <= 54 THEN 1 ELSE 0 END)
WHEN '>=55' THEN SUM(CASE WHEN AGE >= 55 THEN 1 ELSE 0 END)
END Total
FROM
( SELECT
t_personne.pers_date_naissance,
t_personne.pers_date_inscription,
TIMESTAMPDIFF(Year, t_personne.pers_date_naissance, t_personne.pers_date_inscription)
- CASE
WHEN MONTH(t_personne.pers_date_naissance) > MONTH(t_personne.pers_date_inscription)
OR (MONTH(t_personne.pers_date_naissance) = MONTH(t_personne.pers_date_inscription)
AND DAY(t_personne.pers_date_naissance) > DAY(t_personne.pers_date_inscription))
THEN 1 ELSE 0
END AS AGE
FROM t_personne
) AS Total
CROSS JOIN
( SELECT '<18' trage UNION ALL
SELECT '18-24' UNION ALL
SELECT '25-34' UNION ALL
SELECT '35-44' UNION ALL
SELECT '45-54' UNION ALL
SELECT '>=55'
)a
GROUP BY trage
ORDER BY FIELD(trage, '<18', '18-24', '25-34', '35-44', '45-54', '>=55')
it give a table with two columns trage and Total for all categories
How to add a column percentage with a line TOTAL for the column Total and %
Thanks for your help
For the time being, you can't do this. To support this MySQL needs Window Function support which it still doesn't have. If you need functions like these I would recommend switching to PostgreSQL.
Also take a look at this question: MySql using correct syntax for the over clause

SQL Group by "Folder Name" by month and then total

My new query is giving me few errors:
Error: ORA-0093: SQL command not properly ended.
select coalesce(a.group_name, 'Total') as group_name,
sum(case when month (a.sent_date)=1 then a.total_sent else 0 end) as January,
sum(case when month(a.sent_date)=2 then a.total_sent else 0 end) as February,
sum(case when month(a.sent_date)=3 then a.total_sent else 0 end) as March,
sum(case when month(a.sent_date)=4 then a.total_sent else 0 end) as April,
sum(case when month(a.sent_date)=5 then a.total_sent else 0 end) as May,
sum(case when month(a.sent_date)=6 then a.total_sent else 0 end) as June,
sum(case when month(a.sent_date)=7 then a.total_sent else 0 end) as July,
sum(case when month(a.sent_date)=8 then a.total_sent else 0 end) as August,
sum(case when month(a.sent_date)=9 then a.total_sent else 0 end) as September,
sum(case when month(a.sent_date)=10 then a.total_sent else 0 end) as October,
sum(case when month(a.sent_date)=11 then a.total_sent else 0 end) as November,
sum(case when month(a.sent_date)=12 then a.total_sent else 0 end) as December
from c_group a
where a.partner_id=123 AND
a.sent_date >= '01-JAN-2012'
and a.sent_date <= '31-DEC-2012'
group by a.group_name with rollup;
=========
This is my first time posting here and also a beginner at queries.
I am running a query which returns various folder names for all days. I want to group by the folder name and do a sum of the totals for each folder name by months and then a total of each column at the bottom. This is the query I am running:
select a.group_name, a.sent_date, a.total_sent
from
c_group a
where
a.partner_id=123
and a.sent_date >= '01-JAN-2012'
and a.sent_date <= '31-DEC-2012'
Displays as follows:
GROUP_NAME SENT_DATE TOTAL_SENT
Group A 1-Jan-12 37
Group B 3-Jan-12 25
Group C 1-May-12 10
Group D 1-May-12 8
Group D 1-Jan-12 11
Group A 1-Dec-12 9
I need the results to display as:
January February March April May June July August September October November December
Group A
Group B
Group C
Group D
...
....
...
....
Total Sum of above Sum of above Sum of above Sum of above Sum of above Sum of above Sum of above Sum of above Sum of above Sum of above Sum of above Sum of above
You want to combine conditional aggregation with rollup:
select coalesce(a.group_name, 'Total') as group_name,
sum(case when month(a.sent_date) = 1 then a.total_sent else 0 end) as January,
sum(case when month(a.sent_date) = 2 then a.total_sent else 0 end) as February,
. . .
sum(case when month(a.sent_date) = 12 then a.total_sent else 0 end) as December
from c_group a
where a.partner_id = 123 AND
a.sent_date >= '01-JAN-2012' AND
a.sent_date <= '31-DEC-2012'
group by a.group_name with rollup;