Sum multiple columns in multiple rows - mysql

I have following data structure for sales:
id year M01 M02 M03 M04 M05 M06 M07 M08 M09 M10 M11 M12
1 2020 0 5 4 0 10 0 0 0 0 0 0 0
1 2019 4 3 0 0 6 0 7 0 8 0 0 10
2 2020 0 5 4 0 10 0 0 0 0 0 0 0
2 2019 4 3 0 0 6 0 7 0 8 0 0 10
I need to know how many products with id=1 were sold for the last 12 month. If we are in June 2020 I need to sum M01, M02, M03, M04, M05 (WHERE year=2020) and M06, M07, M08, M09, M10, M11, M12 (WHERE year=2019) WHERE id=1. I should get a value of 36.
Please, any suggestions on how to do that in MySQL?

You need to fix your data model. Unpivot and then aggregate:
with reasonable_format as (
select id, year, str_to_date(concat(year, '-01-01'), '%Y-%m-%d') as date, m01 as amount from sales union all
select id, year, str_to_date(concat(year, '-02-01'), '%Y-%m-%d') as date, m02 from sales union all
. . .
select id, year, str_to_date(concat(year, '-02-01'), '%Y-%m-%d') as date, m12 from sales
)
select sum(amount)
from reasonable_format rf
where id = 1 and date <= curdate - interval 1 year;
reasonable_format is what your data should look like.

Related

How to calculate sum of each group last row in mysql

vehicle_assignment_history
id companyAccountId date totalVan totalBike
1 4 2021-11-11 00:00:00 2 0
2 4 2021-11-11 00:00:00 3 0
3 4 2021-11-11 00:00:00 1 0
4 8 2021-11-11 00:00:00 1 0
5 8 2021-11-12 00:00:00 2 0
6 9 2021-11-13 00:00:00 0 2
7 9 2021-11-14 00:00:00 0 1
I want to calculate sum of each group last row of companyAccountId.also the date bewteen a range.
for example:-
2021-11-11 -> 2021-11-13
totalVan totalBike
1 + 2 + 0 = 3 0 + 0 + 2 = 2
2021-11-11 -> 2021-11-14
totalVan totalBike
1 + 2 + 0 = 3 0 + 0 + 1 = 1
One way to do this is to take the max (for each companyAccountId) of a complex string that joins the id and the field you want to find for the highest id, then extract the field you want from the end and convert it back to a number (all in a subquery, so you can sum all the resulting values)
select sum(latestTotalVan) as totalVan, sum(latestTotalBike) as totalBike
from (
select
cast(substring(max(concat(lpad(id,11,'0'),totalVan)) from 12) as unsigned) latestTotalVan,
cast(substring(max(concat(lpad(id,11,'0'),totalBike)) from 12) as unsigned) latestTotalBike
from vehicle_assignment_history
where date between '2021-11-11 00:00:00' and '2021-11-14 00:00:00'
group by companyAccountId
) latest_values
fiddle
mysql 8 adds window functions that make this kind of thing much easier.
SELECT
companyAccountId,
sum(totalVan) AS [Total Vans],
sum(totalBike) AS [Total Bike],
FROM vehicle_assignment_history
GROUP BY companyAccountId
HAVING '2021-11-11' < date AND date < '2021-11-13'

Sort Data in a Range MySQL

I have a table like below
SUBJECT - MARKS - SEMESTER
MATH - 50 - 1
SCIENCE - 60 - 1
ENGLISH - 70 - 1
MATH - 60 - 2
SCIENCE - 80 - 2
ENGLISH - 90 - 2
I want to produce a output like below. The problem is, even there is no data between 0-10 range I want 0 in all three columns. I am unable to achieve using "group by" and "sum". Do any of you have any idea
RANGE MATH SCIENCE ENGLISH
0-10 0 0 0
10-20 0 0 0
20-30 0 0 0
30-40 0 0 0
40-50 0 0 0
50-60 1 0 0
60-70 1 1 0
70-80 0 0 1
80-90 0 1 0
90-100 0 0 1
You can do this, but you need to define the ranges, either as a reference table or in the query. The rest is conditional aggregation:
select r.range,
sum(subject = 'MATH' and t.marks is not null) as Math,
sum(subject = 'SCIENCE' and t.marks is not null) as Science,
sum(subject = 'English' and t.marks is not null) as English
from ((select 0 as mins, 9.99 as maxs, '0-10' as range) union all
(select 10 as mins, 19.99 as maxs, '10-20' as range) union all
. . .
(select 90 as mins, 100 as maxs, '90-100' as range)
) left join
table t
on t.marks between r.mins and r.maxs
group by r.range
order by min(r.mins);

Enter the same value for the entire year

I have table Payments
Client Dt Payment
1 201311 10
1 201312 0
2 201401 0
1 201402 0
1 201403 0
And i want select where i add to this select column "OwnerFlag", where if the client has paid in that year then in all rows for that year for that client will be OwnerFlag 1, otherwise its 0. So the final select should look like :
Client Dt Payment OwnerFlag
1 201311 10 1
1 201312 0 1
2 201401 0 0
1 201402 0 0
1 201403 0 0
Thank you.
Presuming that DT is a datetime column:
SELECT Client, Dt, Payment,
OwnerFlag = CASE WHEN EXISTS
(
SELECT 1 FROM dbo.Payments p1
WHERE p1.ClientID = p.ClientID
AND YEAR(p1.Dt) = YEAR(p.Dt)
AND p1.Pament <> 0
) THEN 1 ELSE 0 END
FROM dbo.Payments p

MySql How to get hourly average count

I have been looking at several different questions related to hourly average queries but I could not find any that addresses the following.
I have a log table that keeps track on how many times a page is accessed by a user:
ID USERID PAGEID SECNO DATE
1 123 120 14 6/08/2013 10:07:29 AM
1 124 438 1 6/08/2013 11:00:01 AM
1 123 211 18 6/09/2013 14:07:59 PM
1 123 120 14 6/10/2013 05:07:18 PM
1 124 312 4 6/10/2013 08:04:32 PM
1 128 81 54 6/11/2013 07:02:15 AM
and I am trying to get two different queries. One that looks like this:
HOURLY Count Average
12am 0 0
1am 0 0
2am 0 0
3am 0 0
4am 0 0
5am 1 0
6am 0 0
7am 1 0
8am 0 0
9am 0 0
10am 1 0
11am 1 0
12pm 0 0
1pm 0 0
2pm 1 0
3pm 0 0
4pm 0 0
5pm 1 0
6pm 0 0
7pm 0 0
8pm 1 0
9pm 0 0
10pm 0 0
11pm 0 0
The second query like this:
DAY PERCENTAGE
Monday 10%
Tuesday 16%
Wednesday 14%
Thursday 22%
Friday 21%
Saturday 14%
Sunday 3%
**Please notice that the average value is just a sample
So far for the first query I have something like this:
SELECT
HOUR(date) AS hourly,
Count(*)
FROM
logs
GROUP BY
hourly
I tried adding AVG() after Count() but did not work.
My log table does not have data for every single hour but i still need to display all the hours on my report. if hour empty, then value 0. Any ideas how could I achieve that?
Try this for the first query:
SELECT
h.hour,
IFNULL(tmp.the_count,0),
IFNULL(tmp.the_avg,0)
FROM
hourly h
LEFT JOIN (
SELECT
hourly,
SUM(visits) the_count,
SUM(visits)/COUNT(DISTINCT userid) as the_avg
FROM (
SELECT
HOUR(date) AS hourly,
COUNT(*) as visits,
userid
FROM
logs
GROUP BY
hourly,
userid
) as tmp
GROUP BY
hourly
) as tmp
ON tmp.hourly = h.hour
Try this for the second query:
SELECT
theday,
IFNULL(percentage,0) as percentage
FROM (
SELECT DATE_FORMAT('2013-06-16','%W') as theday UNION
SELECT DATE_FORMAT('2013-06-16' - INTERVAL 1 DAY,'%W') as theday UNION
SELECT DATE_FORMAT('2013-06-16' - INTERVAL 2 DAY,'%W') as theday UNION
SELECT DATE_FORMAT('2013-06-16' - INTERVAL 3 DAY,'%W') as theday UNION
SELECT DATE_FORMAT('2013-06-16' - INTERVAL 4 DAY,'%W') as theday UNION
SELECT DATE_FORMAT('2013-06-16' - INTERVAL 5 DAY,'%W') as theday UNION
SELECT DATE_FORMAT('2013-06-16' - INTERVAL 6 DAY,'%W') as theday
) as weekt
LEFT JOIN (
SELECT
DATE_FORMAT(date,'%W') AS daily,
(COUNT(*)/(SELECT COUNT(*) FROM logs))/100 as percentage
FROM
logs
WHERE
date >= '2013-06-10'
AND date <= '2013-06-16'
GROUP BY
daily
) as logdata
ON logdata.daily = weekt.theday
SQL has no way to "create" an hour out of nothing. So the simple trick is to have a table numbers (number int) with the numbers you need (may be 1- 31 to be ready for month, or 1-366 for year). That table you can left join with your data in the kind of
select n.number as hour, count(*) as cnt
from numbers as n
left join logtable as l
on hour(l.date) = n.number
group by n.number
You could "simulate" it without a table, but there are several occasions where that table is helpful.

mysql SUM CASE self join

i need some help with this query
this is the actual result of my query
price received qty recieved price release qty release
10.30 10 0 0
0 0 10.30 2
0 0 10.30 1
19.20 20 0 0
0 0 19.20 5
0 0 19.20 3
0 0 19.20 1
Using this code in my query
$result = mysql_query("SELECT *,
SUM(CASE WHEN qtyreceived > 0 THEN qtyreceived END) AS qtyrec,
SUM(CASE WHEN qtyrelease > 0 THEN qtyrelease END) AS qtyrel
FROM stockledger
WHERE stockdesc= '$_POST[desc]' GROUP BY pricerelease,pricereceived ");
the results was
price received qtyreceived price release qtyrelease bal
10.30 10 10
19.20 20 20
10.30 3 -3
19.20 9 -9
and i want to view the result this way so i can get the remaining number for 2 prices
price received qtyreceived price release qtyrelease bal
10.30 10 10.30 3 7
19.20 20 19.20 9 11
thanks.....;
From seeing your data, I think it will be based on the prices. Right? try this one.
SELECT a.PriceReceived,
a.qtyreceived,
b.priceRelease,
b.qtyRelease,
(a.bal + b.bal) as bal
FROM stockledger a
INNER JOIN stockledger b
ON a.priceReceived = b.priceRelease
or another alternative is
SELECT a.PriceReceived,
a.qtyreceived,
b.priceRelease,
b.qtyRelease,
(a.qtyreceived - b.qtyRelease) as bal
FROM stockledger a
INNER JOIN stockledger b
ON a.priceReceived = b.priceRelease
SQLFiddle Demo