Summarize database values of the last 12 hours for each hour - mysql

I've got a table with the columns id, type, value, created_at and I want to sum up the last 12 hours with one query. The result should be in an array for each hour ago.
At the moment I use this query to fetch the data for an hour:
select sum(`value`) as aggregate from `clan_values` where `type` = '2' and `created_at` between '2016-04-12 10:00:00' and '2016-04-12 10:59:59'
I dont have any clue how to solve this with just one query.
Thank you.

you can try using this query, it'll return sum(value) for last 12 hours including the hour of current_timestamp(). If there are no rows for a certain hour you'll get a zero for the aggregate of that hour.
SELECT sum(IFNULL(`value`,0)) as aggregate,
MyTime.MyHour,
DATE_ADD(DATE(created_at),
INTERVAL HOUR(created_at) hour) as ActualHour
FROM
(SELECT DATE_ADD(DATE(CURRENT_TIMESTAMP()),
INTERVAL HOUR(CURRENT_TIMESTAMP())-hour.count hour) as MyHour
FROM
(SELECT 0 as count
UNION SELECT 1
UNION SELECT 2
UNION SELECT 3
UNION SELECT 4
UNION SELECT 5
UNION SELECT 6
UNION SELECT 7
UNION SELECT 8
UNION SELECT 9
UNION SELECT 10
UNION SELECT 11)AS Hour
)as MyTime LEFT JOIN
`clan_values` ON DATE_ADD(DATE(created_at),INTERVAL HOUR(created_at) hour) = MyTime.MyHour
AND `type` = '2'
GROUP BY MyTime.MyHour,ActualHour
ORDER BY MyTime.MyHour ASC
sqlfiddle

You can try group by DATE_FORMAT %H
select sum(`value`) as aggregate
from `clan_values`
where `type` = '2' and `created_at` between '2016-04-12 10:00:00' and '2016-04-12 10:59:59'
group by DATE_FORMAT( `created_at`,'%H');

Related

How to return a row for every date in SQL?

I want to retrieve the sum of transactions for every date from the last 7 days from my MySQL database, but some dates don't have any transactions. How do I return a 0 for those days?
Here is the SQL query I've worked on nd tried, but this one only gives those that do have a value for those days.
SELECT COUNT(transaction_id) AS orders, SUM(amount) AS sales, CAST(time AS DATE) AS time FROM tbltransactions WHERE time BETWEEN CAST(? AS DATE) AND CAST(? AS DATE) GROUP BY CAST(time AS DATE) ORDER BY time ASC
Try to generate the dates first, then join your data table:
SELECT COUNT(transaction_id) AS orders
, SUM(amount) AS sales
, CAST(dates.time AS DATE) AS time
FROM
(
SELECT DATE_SUB(CURDATE(), INTERVAL 7 DAY) + INTERVAL num DAY AS time
FROM
(
SELECT 1 AS num UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
) n
) dates
LEFT JOIN tbltransactions
ON dates.time = tbltransactions.time
GROUP BY CAST(dates.time AS DATE)
ORDER BY dates.time ASC
here is the test data:
CREATE TABLE tbltransactions (
time DATE,
transaction_id INT,
amount DECIMAL(10,2)
);
INSERT INTO tbltransactions (time, transaction_id, amount)
VALUES
('2023-01-20', 1, 100.00),
('2023-01-21', 2, 200.00),
('2023-01-27', 3, 300.00),
('2023-01-29', 4, 400.00),
('2023-01-29', 5, 500.00);

How ot return 0 instead of null on mysql query?

The following query returns the visitors and pageviews of last 7 days. However, if there are no results (let's say it is a fresh account), nothing is returned.
How to edit this in order to return 0 in days that there are no entries?
SELECT Date(timestamp) AS day,
Count(DISTINCT hash) AS visitors,
Count(*) AS pageviews
FROM behaviour
WHERE company_id = 1
AND timestamp >= Subdate(Curdate(), 7)
GROUP BY day
Assuming that you always have at least one record in the table for each of the last 7 days (regardless of the company_id), then you can use conditional aggregation as follows:
select
date(timestamp) as day,
count(distinct case when company_id = 1 then hash end) as visitors,
sum(company_id = 1) as pageviews
from behaviour
where timestamp >= curdate() - interval 7 day
group by day
Note that I changed you query to use standard date arithmetics, which I find easier to understand that date functions.
Otherwise, you would need to move the condition on the date from the where clause to the aggregate functions:
select
date(timestamp) as day,
count(distinct case when timestamp >= curdate() - interval 7 day and company_id = 1 then hash end) as visitors,
sum(timestamp >= curdate() - interval 7 day and company_id = 1) as pageviews
from behaviour
group by day
If your table is big, this can be expensive so I would not recommend that.
Alternatively, you can generate a derived table of dates and left join it with your original query:
select
curdate - interval x.n day day,
count(distinct b.hash) visitors,
count(b.hash) page_views
from (
select 1 n union all select 2 union all select 3 union all select 4
union all select 5 union all select 6 union all select 7
) x
left join behavior b
on b.company_id = 1
and b.timestamp >= curdate() - interval x.n day
and b.timestamp < curdate() - interval (x.n - 1) day
group by x.n
Use a query that returns all the dates from today minus 7 days to today and left join the table behaviour:
SELECT t.timestamp AS day,
Count(DISTINCT b.hash) AS visitors,
Count(b.timestamp) AS pageviews
FROM (
SELECT Subdate(Curdate(), 7) timestamp UNION ALL SELECT Subdate(Curdate(), 6) UNION ALL
SELECT Subdate(Curdate(), 5) UNION ALL SELECT Subdate(Curdate(), 4) UNION ALL SELECT Subdate(Curdate(), 3) UNION ALL
SELECT Subdate(Curdate(), 2) UNION ALL SELECT Subdate(Curdate(), 1) UNION ALL SELECT Curdate()
) t LEFT JOIN behaviour b
ON Date(b.timestamp) = t.timestamp AND b.company_id = 1
GROUP BY day
Use IFNULL:
IFNULL(expr1, 0)
From the documentation:
If expr1 is not NULL, IFNULL() returns expr1; otherwise it returns expr2. IFNULL() returns >a numeric or string value, depending on the context in which it is used.
You can use next trick:
First, get query that return 1 dummy row: SELECT 1;
Next use LEFT JOIN to connect summary row(s) without condition. This join will return values in case data exists on NULL values in other case.
Last select from joined queries onle what we need and convert NULL's to ZERO's
using IFNULL dunction.
SELECT
IFNULL(b.day,0) AS DAY,
IFNULL(b.visitors,0) AS visitors,
IFNULL(b.pageviews,0) AS pageviews
FROM (
SELECT 1
) a
LEFT JOIN (
SELECT DATE(TIMESTAMP) AS DAY,
COUNT(DISTINCT HASH) AS visitors,
COUNT(*) AS pageviews
FROM behaviour
WHERE company_id = 1
AND TIMESTAMP >= SUBDATE(CURDATE(), 7)
GROUP BY DAY
) b ON 1 = 1;

MySQL gett rows from last week with their day name

I am having trouble with a query. This is taken from a similar query where i count number of rows per month.
I want to count all rows for each day of the last week and display the day name and a count. If there are no rows for that day, display zero.
I know the UNIONS won't work but i don't know what to replace it with.
At the moment it get the last 7 days but the day name is NULL
SELECT DAYNAME(STR_TO_DATE(Days.ID, '%a')) AS `day`, COUNT(`returns`.list_date) AS `total`
FROM
(
SELECT 1 as ID UNION SELECT 2 as ID UNION SELECT 3 as ID UNION SELECT 4 as ID
UNION
SELECT 5 as ID UNION SELECT 6 as ID UNION SELECT 7 as ID
) as Days
WHERE (list_date >= DATE_SUB(NOW(), INTERVAL 1 WEEK))
GROUP BY Days.id
UPDATE:
I have created a SQL fiddle showing the code output from #Gordon Linoff answer below which doesn't get the counted rows
http://sqlfiddle.com/#!9/969463/1
One method for doing what you want is a LEFT JOIN:
SELECT DAYNAME(DATE_SUB(CURDATE(), INTERVAL Days.n DAY)) AS `day`,
COUNT(r.list_date) AS `total`
FROM (SELECT 1 as n UNION ALL SELECT 2 as n UNION ALL
SELECT 3 as n UNION ALL SELECT 4 as n UNION ALL
SELECT 5 as n UNION ALL SELECT 6 as n UNION ALL
SELECT 7 as n
) Days LEFT JOIN
returns r
ON r.list_date = DATE_SUB(CURDATE(), INTERVAL Days.n DAY))
GROUP BY Days.n
ORDER BY Days.n;
Some notes:
Use UNION ALL instead of UNION, unless you have a good reason for incurring the overhead of removing duplicates.
This assumes that returns.list_date is actually a date, because it uses = rather than >=.
The use of now() and >= is a bit confusing, because now() has a time component, which you generally want to ignore.

Hour Wise data in mySql

I have the table with following fields
Createdon(datetime)
Amount(double)
I need to find the sum of amounts for next 24 hours of the given date. If there are no results then the sum should be zero.
e.g
duration sum
0000-0001 25.43
0001-0002 36.85
0002-0003 0
.
.
.
.
0022-0023 38.56
Can you please help me creating a query to find the required solution
The key to your query is the ability to take any datetime value and truncate it to the nearest preceding hour. You can do that with this expression:
DATE_FORMAT(Createdon, '%Y-%m-%d %H:00')
Given, for example, 2015-04-21 14:22:05, this gives back 2015-04-21 14:00:00.
Then you use that in GROUP BY
SELECT DATE_FORMAT(Createdon, '%Y-%m-%d %H:00') Createdhour,
SUM(Amount) sum
FROM theTable
GROUP BY DATE_FORMAT(Createdon, '%Y-%m-%d %H:00')
ORDER BY DATE_FORMAT(Createdon, '%Y-%m-%d %H:00')
Finally, I think you wanted one day's worth of results. You need to add a WHERE clause to get that. The one shown here will take yesterday's results -- that is, all results from [midnight yesterday -- midnight today).
SELECT DATE_FORMAT(Createdon, '%Y-%m-%d %H:00') Createdhour,
SUM(Amount) sum
FROM theTable
WHERE CreatedOn >= DATE(NOW()) - INTERVAL 1 DAY
AND CreatedOn < DATE(NOW())
GROUP BY DATE_FORMAT(Createdon, '%Y-%m-%d %H:00')
ORDER BY DATE_FORMAT(Createdon, '%Y-%m-%d %H:00')
This is explained in greater detail at http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/
To include all hours of the day, you will need an independent source of distinct DATETIME items.
Here's a query that will do such a thing.
SELECT mintime + INTERVAL seq.seq HOUR AS CreatedHour
FROM (
SELECT DATE(NOW()) - INTERVAL 1 DAY AS mintime,
DATE(NOW()) AS maxtime
) AS minmax
JOIN seq_0_to_23 AS seq
ON seq.seq < TIMESTAMPDIFF(HOUR,mintime,maxtime)
You then need to use LEFT JOIN to pick up your data.
SELECT a.Createdhour,
SUM(Amount) sum
FROM (
SELECT mintime + INTERVAL seq.seq HOUR AS CreatedHour
FROM (
SELECT DATE(NOW()) - INTERVAL 1 DAY AS mintime,
DATE(NOW()) AS maxtime
) AS minmax
JOIN seq_0_to_23 AS seq
ON seq.seq < TIMESTAMPDIFF(HOUR,mintime,maxtime)
) a
LEFT JOIN theTable t
ON a.CreatedHour = DATE_FORMAT(t.Createdon, '%Y-%m-%d %H:00')
GROUP BY DATE_FORMAT(t.Createdon, '%Y-%m-%d %H:00')
ORDER BY DATE_FORMAT(t.Createdon, '%Y-%m-%d %H:00')
Finally, you need to somehow get that table seq_0_to_23. If you're running MariaDB, it's built in. If not...
CREATE TABLE seq_0_to_23 AS
SELECT 0 AS seq
UNION ALL SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6
UNION ALL SELECT 7 UNION ALL SELECT 8 UNION ALL SELECT 9
UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15
UNION ALL SELECT 16 UNION ALL SELECT 17 UNION ALL SELECT 18
UNION ALL SELECT 19 UNION ALL SELECT 20 UNION ALL SELECT 21
UNION ALL SELECT 22 UNION ALL SELECT 23
This is written up in more general form at http://www.plumislandmedia.net/mysql/filling-missing-data-sequences-cardinal-integers/

MySQL - Select number of entries the last 7 days

This is how my table looks like
-----------------------
posts
----------------------
id
created_at
..
..
How should the MySQL Query look like, so that i get the number of entries the last 7 days.
The result should look something like that:
['Mon' => 234, 'Tues' => 12, ...]
you can use datediff for that
select count(*), extract(day from created_at) current_day
from posts
where datediff(now(), created_at) <= 7
group by current_day;
If you want 0 values for other day, then you would have to use a left join on a fake table that generate last seven days.
select count(posts.created_at) as nb_occurence, c.a as number_of_day
from
(select b.a
from (select 1 a
union all select 2 a
union all select 3 a
union all select 4 a
union all select 5 a
union all select 6 a
union all select 7 a) b) c
left join posts on extract(day from posts.created_at) % 7 - c.a in (select extract(day from date_sub(created_at, INTERVAL 7 DAY)) % 7 from posts)
group by c.a;
SELECT COUNT(*)
FROM posts
WHERE created_at<xy
GROUP BY created_at
SELECT COUNT(*), DATE_FORMAT(created_at,'%a')
FROM posts
WHERE created_at <= NOW() AND created_at >= DATE_SUB(created_at, INTERVAL 7 DAY)
GROUP BY DATE_FORMAT(created_at,'%a')
To add 0 for weekday having null in count:
SELECT a.weekday, IFNULL(b.total,0) FROM
(SELECT 'Mon' as weekday from dual union SELECT 'Tue' as weekday from dual union SELECT 'Wed' as weekday from dual union SELECT 'Thu' as weekday from dual union SELECT 'Fri' as weekday from dual union SELECT 'Sat' as weekday from dual union SELECT 'Sun' as weekday from dual) a
LEFT JOIN
(SELECT COUNT(*) as total, DATE_FORMAT(created_at,'%a') as weekday
FROM posts
WHERE created_at <= NOW() AND created_at >= DATE_SUB(created_at, INTERVAL 7 DAY)
GROUP BY DATE_FORMAT(created_at,'%a')) b on a.weekday=b.weekday