Mysql picking the greatest columna after aggregation - mysql

I have a table of sold items, with specified name, day, hour and amount of items sold.
What I need to do is for every day find the hour in which the greatest number of items (of any type) was sold and return the two-columned table with day and amount of items.
What I managed to do is to compute the sum of items per hour, but how to pick the hour with maximum amount of items sold and show it together with the day?
here is my lousy sqlfiddle attempt: http://sqlfiddle.com/#!9/93b51/17/0
select day, hour, sum(amount) as suma
from sold_items
group by day, hour

You need to join your query with juergen d's query that gets the maximum hourly amount each day.
SELECT a.day, a.hour, a.suma
FROM (
select day, hour, sum(amount) as suma
from sold_items
group by day, hour) AS a
JOIN (
select day, max(suma) AS maxsuma
from (
select day, hour, sum(amount) as suma
from sold_items
group by day, hour) AS tmp
group by day) AS b
ON a.day = b.day AND a.suma = b.maxsuma
DEMO
This follows the same pattern as SQL Select only rows with Max Value on a Column except that in this case, you're doing it with a subquery that calculates an aggregate, not the data coming directly from the table.

select day, max(suma)
from
(
select day, hour, sum(amount) as suma
from sold_items
group by day, hour
) tmp
group by day
SQLFiddle

Related

How to find the avg number of trips per day for each month in one year in MySQL

I was given id, start_date, end_date in the dataset and was asking to find the average number of trips per day for each month in MySQL.
My query looks like:
SELECT
YEAR(start_date) AS Year,
MONTH(start_date) AS Month,
COUNT(*) / (COUNT(DISTINCT YEAR(start_date))) AS avg_trips_per_day
FROM
trips
GROUP BY Year , Month
ORDER BY Year , Month;
But the returning result for avg_trips_per_day was the total number of trips in that month instead. Any suggestions?
This shows you the average trips per travelled days for every Month and year
Spo when there where only five days this month with different startdate, it calculates the avg for every travelled day
SELECT
YEAR(start_date) AS 'Year',
MONTH(start_date) AS 'Month',
COUNT(*) / COUNT(DISTINCT start_date) avg_trips_per_day
FROM trips
GROUP BY YEAR(start_date),MONTH(start_date)
ORDER BY YEAR(start_date),MONTH(start_date);
See dbfiddle
If you want average number of trips per day for a month, you need to ultimately divide the number of trips made in a month by the number of days in that month. At least that is my understanding of what average number of trips per day for a month mean (i.e. the number of days in the month has to factor into the calculation). If I made 3 trips on 1/1/2020 and no other trips for the rest of the year, that would be an average of 3 trips per year. It would also be an average of 3 trips per month for the month of January, 2020. But it can only be 3/31 trips per day for the entire month.
If you only wanted to divide by the number of days on which trips were actually made, then the question should be worded, "How do you find the average number of trips per trip-days for each month ..." or words to that effect.
Also, I am not sure how you want to handle a trip that begins on one day and ends on another. This SQL just looks at the start date since that is what yours is doing:
select year(start_date) as start_year,
month(start_date) as start_month,
count(*) /
(select day(last_day(concat(start_year, '-', start_month, '-01')))) as avg
from trips
group by start_year, start_month
order by start_year, start_month
The expression select day(last_day(concat(start_year, '-', start_month, '-01'))) calculates the number of days in the month given by year start_year and month start_month, which is needed because the average number of trips per day for a month is the total number of trips for the month divided by the number of days in the month.
See Db Fiddle
You only need to change one word from YEAR to DAY or DATE.
SELECT
YEAR(start_date) AS Year,
MONTH(start_date) AS Month,
COUNT(*) / (COUNT(DISTINCT DAY(start_date))) AS avg_trips_per_day
FROM
trips
GROUP BY Year , Month
ORDER BY Year , Month;
OR
SELECT
YEAR(start_date) AS Year,
MONTH(start_date) AS Month,
COUNT(*) / (COUNT(DISTINCT DATE(start_date))) AS avg_trips_per_day
FROM
trips
GROUP BY Year , Month
ORDER BY Year , Month;
The reason why your result was the sum of all trips per month is because the
result of COUNT(DISTINCT YEAR(start_date)) is 1
So you could run both the queries below to view the difference.
SELECT
YEAR(start_date) AS Year,
MONTH(start_date) AS Month,
COUNT(*) AS total_trips_of_month,
COUNT(DISTINCT DAY(start_date)) AS days_having_trips_that_month,
COUNT(*) / (COUNT(DISTINCT YEAR(start_date))) AS avg_trips_per_day
FROM
trips
GROUP BY Year , Month
ORDER BY Year , Month
vs
SELECT
YEAR(start_date) AS Year,
MONTH(start_date) AS Month,
COUNT(*) AS total_trips_of_month,
COUNT(DISTINCT DAY(start_date)) AS days_having_trips_that_month,
COUNT(*) / (COUNT(DISTINCT DATE(start_date))) AS avg_trips_per_day
FROM
trips
GROUP BY Year , Month
ORDER BY Year , Month

Incrementing MySql variable by SUM() not correct

I am trying to put together a query that groups records by date along with a total for that particular date (there can be multiple entries in a day) but I also need a running total that I intended on using a MySQL variable for. My issue is that the cumulative total column seems to contain the SUM() for just that date.
So this works fine for the daily totals
SELECT
YEAR(log_at) as year,
MONTH(log_at) as month,
DAY(log_at) as day,
SUM(ev) as dailyTotal,
count(*) as count
FROM tracks
WHERE user_id = 1
GROUP BY year, month, day
ORDER BY year, month, day ASC
Add what I thought was going to be a fairly simple variable in there to keep track of the running total
SET #cumulative := 0;
SELECT
YEAR(log_at) as year,
MONTH(log_at) as month,
DAY(log_at) as day,
SUM(ev) as dailyTotal,
#cumulative := #cumulative + sum(ev) AS cumulative,
count(*) as count
FROM tracks
WHERE user_id = 1
GROUP BY year, month, day
ORDER BY year, month, day ASC
And the cumulative variable just contains the total for that day. But if I change it to increment by 1 instead of the SUM() it seems to work correctly
Any advice for achieving the desired behaviour is greatly appreciated!
You have to first create the whole selection and only then do your cumulative-stuff because you can't append the sum of a column of your GROUP BY at the same time (I don't really know why dough)
SELECT
`year`,
`month`,
`day`,
`dailyTotal`,
#cum := #cum + `dailyTotal` as `cumulative`,
`count`
FROM
(
SELECT
YEAR(log_at) as year,
MONTH(log_at) as month,
DAY(log_at) as day,
SUM(ev) as dailyTotal,
count(*) as count
FROM tracks
WHERE user_id = 1
GROUP BY year, month, day
ORDER BY year, month, day ASC
) a
JOIN (SELECT #cum := 0) b
I tested it with a bit less intensive tables... maybe I got this one wrong here. But I hope you get the theory.

Can I get every month of the year even if there is not data for that month in DB

I want to get a statistic for every month of the years i have in DB
SELECT monthname(created_at) AS month, YEAR(created_at) AS year, count(*) AS number
FROM tableName
WHERE type_of_user = "someType"
GROUP BY year, month(created_at)
ORDER BY created_at DESC
Now it gives me only month that I have, but I need to get statistics for every month, even if I don't have any stored data for that month
Create a calendar table. This will need one entry per month, for every year that you intend to use.
Then select from the calendar table, and join in the values that you get from your current query. Use COALESCE() to put a zero-value where the entry is NULL (e.g. when there are no records in the tableName for that month and year).
SELECT MONTHNAME(date) as month,
YEAR(date) as year,
COALESCE(number, 0) as number
FROM calendar AS C
LEFT JOIN (
SELECT created_at, COUNT(*) as number
FROM tableName AS T
WHERE T.type_of_user = 'someType'
GROUP BY YEAR(created_at), MONTH(created_at)
) AS T
ON MONTH(T.created_at) = MONTH(C.date) AND YEAR(T.created_at) = YEAR(C.date)
GROUP BY month, YEAR(created_at)
ORDER BY MONTH(date), YEAR(date)
SQL fiddle at http://sqlfiddle.com/#!9/e0a4dc/
SELECT month(created_at) as month
FROM tableName
RIGHT JOIN (select row_number() over (order by 1) as i
from someTableWithMoreThan12Records limit 12) x
ON x.i=month(created_at)
ORDER BY I;
JOINing with a table that has all the records will give you every month.

Number of logins during hourly, each day of the week?

I have a list of data contians (yyyy-mm-dd, hh:min:ss) from this data i need to sort out how many times 'logins' was punched for any given hour of the day. and also how many times logins' was punched for each day of the week .
I tried some code but am not confident the code i have right. Also should i include COUNT(*) in the statement
This code for hourly logins :
SELECT date_time, HOUR(date_time) FROM time_logs ORDER BY DAY(date_time);
Code for day for week:
SELECT date_time, DAY(date_time) FROM time_logs ORDER BY DAY(date_time);
Is this is right. If not could you give me hint
To count how many logins per hour, you just need to add COUNT(*) in your SELECT and GROUP BY HOUR(date_time).
SELECT date_time, HOUR(date_time),COUNT(*) 'Total Logins'
FROM time_logs GROUP BY HOUR(date_time) ORDER BY DAY(date_time);
Similarly on your day count you need to add the same thing. Only difference is your grouping is now by day.
SELECT date_time, DAY(date_time), COUNT(*) 'Total Logins'
FROM time_logs GROUP BY DAY(date_time) ORDER BY DAY(date_time);
You can cast your datetime to both hour and date and then group by to get your aggregations.
https://rextester.com/BQSV80644
For hour of the day:
SELECT HOUR(date_time), COUNT(*)
FROM time_logs
GROUP BY HOUR(date_time);
For day of the week:
SELECT WEEKDAY(date_time), COUNT(*)
FROM time_logs
GROUP BY WEEKDAY(date_time)
ORDER BY MIN(date_time)

MySQL Join tables on weekday+hour of timestamp, count per table

I have a couple of tables with exact the same structure. They contain data from forms filled in at the entrance of a yearly event. One of the fields in these tables is a DATETIME with the date and time of the entry.
For statistical purposes, I am trying to compare the amount of entries per hour of each year. Of course, I could run separate queries for each table and put them together in PHP. But I think it should also be possible in one single query. However, I cannot figure out how to build up a correct query.
This is what I've got so far:
SELECT
WEEKDAY(P3.AddedOn) Day,
HOUR(P3.AddedOn) Hour,
COUNT(P3.AddedOn) Entries2013,
COUNT(P2.AddedOn) Entries2012
FROM Event2013 P3
LEFT JOIN Event2012 P2
ON WEEKDAY(P3.AddedOn) = WEEKDAY(P2.AddedOn)
AND HOUR(P3.AddedOn) = HOUR(P2.AddedOn)
GROUP BY WEEKDAY(P3.AddedOn), WEEKDAY(P2.AddedOn), HOUR(P3.AddedOn), HOUR(P2.AddedOn)
But this query yields some strange results with too large numbers and the same numbers in the Entries2012 and Entries2013 columns. It adds up some data, but I cannot figure out exactly which.
What am I doing wrong?
Thank you for your help!
--
Solved with subqueries:
SELECT
P2.Day,
P2.Hour,
P2.Entries Entries2012,
P3.Entries Entries2013
FROM
(
SELECT
WEEKDAY(AddedOn) Day,
HOUR(AddedOn) Hour,
COUNT(1) AS Entries
FROM Event2012
GROUP BY Day, Hour
) P2
LEFT JOIN
(
SELECT
WEEKDAY(AddedOn) Day,
HOUR(AddedOn) Hour,
COUNT(1) AS Entries
FROM Event2013
GROUP BY Day, Hour
) P3
ON P2.Day = P3.Day
AND P2.Hour = P3.Hour
--
Now also with some kind of MySQL FULL OUTER JOIN:
SELECT
Day,
Hour,
Entries2012,
Entries2013
FROM (
SELECT
(CASE P2.Day
WHEN 4 THEN "Fr"
WHEN 5 THEN "Sat"
WHEN 6 THEN "Sun"
ELSE P2.Day
END) AS Day,
P2.Hour,
COALESCE(P2.Entries, 0) Entries2012,
COALESCE(P3.Entries, 0) Entries2013
FROM (
SELECT
WEEKDAY(AddedOn) Day,
HOUR(AddedOn) Hour,
COUNT(1) AS Entries
FROM Event2012
GROUP BY Day, Hour) P2
LEFT JOIN (
SELECT
WEEKDAY(AddedOn) Day,
HOUR(AddedOn) Hour,
COUNT(1) AS Entries
FROM Event2013
GROUP BY Day, Hour) P3
ON P2.Day = P3.Day
AND P2.Hour = P3.Hour
UNION SELECT
(CASE P3.Day
WHEN 4 THEN "Fr"
WHEN 5 THEN "Sat"
WHEN 6 THEN "Sun"
ELSE P3.Day
END) AS Day,
P3.Hour,
COALESCE(P2.Entries, 0) Entries2012,
COALESCE(P3.Entries, 0) Entries2013
FROM (
SELECT
WEEKDAY(AddedOn) Day,
HOUR(AddedOn) Hour,
COUNT(1) AS Entries
FROM Event2012
GROUP BY Day, Hour) P2
RIGHT JOIN (
SELECT
WEEKDAY(AddedOn) Day,
HOUR(AddedOn) Hour,
COUNT(1) AS Entries
FROM Event2013
GROUP BY Day, Hour) P3
ON P2.Day = P3.Day
AND P2.Hour = P3.Hour
WHERE P2.Hour IS NULL) AS tmp
ORDER BY Day, Hour
LEFT JOIN says that you intend to take all entries from P3 and expect that some of them won't match in P2. If P3 is your base table (and you look for possible matches in another table), you only have to group by P3 columns.
SELECT P3.Day Day, P3.Hour Hour, Entries2013, Entries2012
FROM (
SELECT
WEEKDAY(AddedOn) Day,
HOUR(AddedOn) Hour,
COUNT(AddedOn) Entries2013
GROUP BY Day, Hour
FROM Event2013) P3
LEFT JOIN (
SELECT
WEEKDAY(AddedOn) Day,
HOUR(AddedOn) Hour,
COUNT(AddedOn) Entries2012
GROUP BY Day, Hour
FROM Event2012) P2
ON P3.DAY = P2.DAY
AND P3.Hour = P2.Hour
GROUP BY P3.Day, P3.Hour;
Also, using WEEKDAY means you sum all Mondays, Tuesdays etc in the given year and then get hourly summaries from that. If you want to get daily summaries (where the day is the day of the year), you should use DAYOFYEAR i/o WEEKDAY.
Look at the UNION statement: http://dev.mysql.com/doc/refman/5.0/en/union.html
This combines queries from two tables.