Options (my table)
id datetime energy
1 2014-10-28 04:00:00 14
1 2014-10-28 04:05:00 16
1 2014-10-28 04:10:00 23
1 2014-10-28 04:15:00 45
1 2014-10-29 04:00:00 34
1 2014-10-29 04:05:00 33
1 2014-10-29 04:10:00 12
1 2014-10-29 04:15:00 67
output
id datetime
1 2014-10-28 04:15:00 28
1 2014-10-29 04:15:00 37.33
my query:
SELECT date(`datetime`) dateDay,id,
15*floor(date_format(`datetime`,'%i')/15) dateHour,
avg(energy) FROM `meter`
WHERE `datetime` >= '2014-10-28 00:00:01' AND `datetime` <= '2014-10-29 23:59:59'
GROUP BY id,day(datetime),month(datetime),dateHour
If I understand, you want to achieve a grouping by 15 minutes intervals. If so, you would get along with something like this:
SELECT
id,
avg(energy) as average_value,
date_format(datetime, "%Y-m-d") as date_day,
date_format(datetime,'%H') as date_hour
IF(date_format(datetime,'%i') < 15, 0,
IF(date_format(datetime,'%i') <= 30, 15,
IF(date_format(datetime,'%i') <= 45, 30,45))) as fifteen_minutes_slot
from deliverydestination
GROUP BY id, date_day, date_hour, fifteen_minutes_slot
1) compute the fifteen_minutes slots with a set of ifs: if the value for minutes is less than 15, give it the value 0, else if it is less than 30, give it the value 15, etc...
2) group by day, hour and 15 minutes slot value. And by id, as in your example, if needed.
It would be nice to have a fiddle with your data, to show the final result...
Related
Data sample:
dtime
id
2021-01-01 06:00:00
1
2021-01-01 06:00:00
2
2021-01-01 06:00:00
3
...
...
2021-01-01 12:00:00
1
2021-01-01 12:00:00
2
2021-01-01 12:00:00
3
...
...
...
...
2021-01-12 20:00:00
1
2021-01-12 20:00:00
2
2021-01-12 20:00:00
3
In the real dataset, ids are between 1 and 9999, dtime are every 5 minutes, 24h/day, and I'd like to sample only at certain times (eg 06, 12, 16, 20h).
The expected output is the average of count(id) values, grouped by DATE(dtime), but:
Only certain TIME(dtime) should be sampled (eg 06, 12, 16, 20h);
count(id) should ignore id that are not between 10 and 500;
count(id) should be discarded (and not considered for the average) if <3.
Output sample:
DATE(dtime)
AVG(count(id))
2021-01-01
31
2021-01-02
29
So far I've got:
SELECT dtime,count(id)
FROM cron5min
WHERE (TIME(dtime) = '06:00:00' OR TIME(dtime) = '12:00:00' OR TIME(dtime) = '16:00:00' OR TIME(dtime) = '20:00:00') AND id BETWEEN 10 AND 500 AND estado = 1
GROUP BY dtime
and then I'm using PHP to do the average and discard data according to 3.
I'm now trying to do this with a MySQL statement only, no PHP.
You need 2 levels of aggregation:
SELECT DATE(dtime) date, AVG(counter) avg_count
FROM (
SELECT dtime, COUNT(id) counter
FROM cron5min
WHERE TIME(dtime) IN ('06:00:00', '12:00:00', '16:00:00', '20:00:00')
AND id BETWEEN 10 AND 500
AND estado = 1
GROUP BY dtime
HAVING counter >= 3
) t
GROUP BY date
My sale Table
ID amount created_at
48 10 2018-10-15 10:57:24
49 20 2018-10-16 10:58:14
50 25 2018-10-22 14:07:31
51 24 2018-10-24 12:13:15
52 36 2018-10-24 12:13:21
53 40 2018-10-30 09:46:37
54 40 2018-10-28 09:46:37
55 40 2018-11-1 09:46:37
56 40 2018-11-2 09:46:37
57 40 2018-11-2 09:46:37
58 40 2018-11-2 09:46:37
59 40 2018-11-2 09:46:37
60 40 2018-11-2 09:46:37
My qyery
SELECT Date(created_at),
Count(*)
FROM sale
WHERE Date(created_at) BETWEEN ( Now() - INTERVAL 7 day ) AND Now()
GROUP BY Date(created_at)
My result
date(created_at) count
2018-10-28 12:13:15 1
2018-10-1 09:46:37 1
2018-10-2 09:46:37 5
Suppose Week Start from 2018-10-28 and i need result like below if there is no record of particular day then it will 0.
day count
mon 1
tue 0
wed 0
thu 1
fri 5
sat 0
sun 0
Firstly, you can create a Master Table representing all the Abbreviated Weekday name(s).
Now, we can use Date_format() function with %a specifier to get the abbreviated weekday name for a particular created_at date. We can use this as our Left Join condition.
Left Join allows us to consider all the Weekdays, even if there is no matching created_at for a particular day.
Count(*) will not work here, as it counts all the rows in a group. However, we don't want to count the rows where there is no matching rows. So, we use Count(created_at), as it will ignore the null values.
Finally, query is made sargable by removing Date() function usage in the Where clause.
You will need to use a master table for week days.
Query:
SELECT week_table.day_order,
week_table.wkday AS `day`,
Count(created_at) AS `count`
FROM
(
SELECT 'Mon' AS wkday, 1 AS day_order UNION ALL
SELECT 'Tue', 2 UNION ALL
SELECT 'Wed', 3 UNION ALL
SELECT 'Thu', 4 UNION ALL
SELECT 'Fri', 5 UNION ALL
SELECT 'Sat', 6 UNION ALL
SELECT 'Sun', 7
) AS week_table
LEFT JOIN sale AS s
ON DATE_FORMAT(created_at, '%a') = week_table.wkday AND
created_at >= ( CURDATE() - INTERVAL 7 day ) AND
created_at < ( CURDATE() + INTERVAL 1 day )
GROUP BY week_table.day_order, week_table.wkday
ORDER BY week_table.day_order;
Result
| day_order | day | count |
| --------- | --- | ----- |
| 1 | Mon | 0 |
| 2 | Tue | 1 |
| 3 | Wed | 0 |
| 4 | Thu | 1 |
| 5 | Fri | 5 |
| 6 | Sat | 0 |
| 7 | Sun | 1 |
View on DB Fiddle
My table have fields that represent starting and ending working period as datetime.
I need to find related entries that match a total of 14hours min over a sliding period of 24 hours.
I think window function will (maybe) save me, but MariadDB (i use) doesn't implement yet Range time intervals in window function.
here is some example data:
id starting_hour ending_hour
-- ------------------- -------------------
1 2018-09-02 06:00:00 2018-09-02 08:30:00
2 2018-09-03 08:30:00 2018-09-03 10:00:00
4 2018-09-03 11:00:00 2018-09-03 15:00:00
5 2018-09-02 15:30:00 2018-09-02 16:00:00
6 2018-09-02 16:15:00 2018-09-02 17:00:00
7 2018-09-20 00:00:00 2018-09-20 08:00:00
8 2018-09-19 10:00:00 2018-09-19 12:00:00
9 2018-09-19 12:00:00 2018-09-19 16:00:00
10 2018-10-08 12:00:00 2018-10-08 14:00:00
11 2018-10-29 09:00:00 2018-10-29 10:00:00
So how to find rows where in a 24 hours window their sum a more or equal to 14 hours.
thanks
Edit:
SELECT
id,
starting_hour,
ending_hour,
TIMEDIFF (ending_hour, starting_hour) AS duree,
(
SELECT SUM(TIMEDIFF(LEAST(ending_hour, DATE_ADD(a.starting_hour, INTERVAL 24 HOUR)), starting_hour)) / 10000
FROM `table` b
WHERE b.starting_hour BETWEEN a.starting_hour AND DATE_ADD(a.starting_hour, INTERVAL 24 HOUR)
) AS duration
FROM
`table` a
HAVING duration >= 14
ORDER BY starting_hour ASC
;
This returns Id 8 but i want the whole period. (eg: Id 8, Id 9 and Id 7)
EDIT2:
The expected results are ranges of working time where they are in a window of 24 hours and where their sum are more or equal to 14 hours.
EDIT 3:
In fact under MySQL 8 this seems to work.
SELECT * FROM (
SELECT
*,
SEC_TO_TIME(SUM(TIME_TO_SEC(TIMEDIFF(hs.`ending_hour`, hs.`starting_hour`))) OVER (ORDER BY hs.starting_hour RANGE BETWEEN INTERVAL '12' HOUR PRECEDING AND INTERVAL '12' HOUR following)) AS tot
FROM
table hs
WHERE hs.`starting_hour` > DATE_SUB(NOW(), INTERVAL 50 DAY) AND hs.`ending_hour` <= NOW()
ORDER BY hs.`starting_hour` ASC
) t1
HAVING tot >= '14:00:00'
;
Is there a way to do it under MariaDB 10.2 without window function ? Or without window range function ?
I have "alerts" table with date field - targetDate.
I would like to select all data that past 45 days.
I tried the code below but it's not return any results...
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon'
AND DATEDIFF( CURDATE( ) , targetDate ) > 45
Table
id userID type refID createDate targetDate lastSendDate sent valid
1 26 travelSoon NO 2018-05-02 13:54:25 0000-00-00 2018-05-02 00:00:00 0 1
2 26 travelSoon NO 2018-05-02 13:55:50 2018-06-01 0000-00-00 00:00:00 0 1
3 26 travelSoon DK 2018-05-02 13:56:12 2018-12-01 0000-00-00 00:00:00 0 1
4 26 travelSoon 2018-05-02 13:59:50 0000-00-00 0000-00-00 00:00:00 0 1
5 26 travelSoon 2018-05-02 14:00:09 2018-08-01 0000-00-00 00:00:00 0 1
6 26 travelSoon DK 2018-05-02 14:00:48 2018-08-01 0000-00-00 00:00:00 0 1
7 26 travelSoon 2018-05-02 16:45:18 2018-05-01 0000-00-00 00:00:00 0 1
8 26 travelSoon RO 2018-05-02 16:45:45 2018-04-01 0000-00-00 00:00:00 0 1
Using DATEDIFF() is a bad idea. It blocks the ability to use indexes, and there is an alternative that doesn't...
SELECT *
FROM alerts
WHERE type = 'travelSoon'
AND targetDate >= DATEADD(DAY, -45, GETDATE()) -- SQL Server
AND targetDate >= CURDATE() - INTERVAL 45 DAY -- MySQL
http://www.sqlfiddle.com/#!9/4ecdc0/6
In MSSQL DATEDIFF(interval, date1, date2) returns interval of date2 - date1.
Interval should be selected from this list:
- year, yyyy, yy = Year
- quarter, qq, q = Quarter
- month, mm, m = month
- dayofyear = Day of the year
- day, dy, y = Day
- week, ww, wk = Week
- weekday, dw, w = Weekday
- hour, hh = hour
- minute, mi, n = Minute
- second, ss, s = Second
- millisecond, ms = Millisecond`
Then use:
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon'
AND DATEDIFF(day, targetDate, GETDATE() ) > 45
For MySQL you can use TIMESTAMPDIFF(unit,date1,date2) which returns interval of date1 - date2.
unit can be selected from MICROSECOND (microseconds), SECOND, MINUTE, HOUR, DAY, WEEK, MONTH, QUARTER, or YEAR.
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon'
AND TIMESTAMPDIFF(DAY, CURDATE( ), targetDate) > 45
The ANSI Standard syntax would be:
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon' AND
targetDate >= CURRENT_DATE - interval '45 day' AND
targetDate <= CURRENT_DATE;
In MySQL (which your syntax suggests:
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon' AND
targetDate >= CURRENT_DATE - interval 45 day AND
targetDate <= CURRENT_DATE;
Try this...
SELECT userid, refid, `targetdate`
FROM alerts
WHERE type = 'travelSoon'
AND Datediff(Curdate(), targetDate) < 45 -- or <=45
AND Datediff(Curdate(), targetDate) > 0
Online Demo: http://www.sqlfiddle.com/#!9/4ecdc0/4/0
If you only use Datediff(Curdate(), targetDate) < 45 condition, it may return both past and future dates. Please refer the below table.
Today: May 10, 2018
+----+-------------+
| id | targetDate | DATEDIFF(CURDATE(), targetDate)
+----+-------------+
| 2 | 𝟮𝟬𝟭𝟴-𝟬𝟲-𝟬𝟭 | -22
| 3 | 𝟮𝟬𝟭𝟴-𝟭𝟮-𝟬𝟭 | -205
| 5 | 𝟮𝟬𝟭𝟴-𝟬𝟴-𝟬𝟭 | -83
| 6 | 𝟮𝟬𝟭𝟴-𝟬𝟴-𝟬𝟭 | -83
| 7 | 2018-05-01 | 9
| 8 | 2018-04-01 | 39
+----+-------------+
To avoid this, you can use another condition like this...
Datediff(Curdate(), targetDate) > 0
SELECT userID, refID, `targetDate`
FROM alerts
WHERE type = 'travelSoon'
AND targetDate >= ( CURDATE() - INTERVAL 45 DAY )
From a Call Detail Record (CDR) table, I need to compute the maximum channels that have been simultaneously busy in a specified time range. The table has one column for the end of the call time and another column for the call duration.
CallID DateTime Duration
1 2014-04-28 11:37:54 35
2 2014-04-28 11:37:53 82
3 2014-04-28 11:37:42 53
4 2014-04-28 11:37:37 159
5 2014-04-28 11:37:13 49
6 2014-04-28 11:37:02 267
7 2014-04-28 11:37:00 267
8 2014-04-28 11:36:54 49
9 2014-04-28 11:36:20 266
I would need a report for getting a response like this (time range in hours for example):
DateRange NumBusyChannels
2014-04-28 11:00-12:00 9
2014-04-28 10:00-11:00 5
EDIT: Sample table to test Miky response.
CallID DateTime Duration
014 2013-07-23 08:42:42 3
015 2013-07-23 08:42:42 3
019 2013-07-23 08:42:42 3
012 2013-07-23 08:10:00 3
013 2013-07-23 08:10:00 3
016 2013-07-23 08:10:00 3
017 2013-07-23 08:10:00 3
018 2013-07-23 08:10:00 3
You might start with a query like this which should give you the number of overlapping calls for each call.
SELECT COuter.*,
COALESCE(COverlap.Cnt,0) as NumChannelsInUse
FROM Cdr COuter
LEFT OUTER JOIN(
SELECT C1.CallId,
COUNT(*) as Cnt
FROM Cdr C1
INNER JOIN Cdr C2
ON (C2.DateTime >= C1.DateTime
AND
C2.DateTime <= DATE_ADD(C1.DateTime, INTERVAL C1.Duration SECOND))
OR
(DATE_ADD(C2.DateTime, INTERVAL C2.Duration SECOND) >= C1.DateTime
AND
DATE_ADD(C2.DateTime, INTERVAL C2.Duration SECOND) <= DATE_ADD(C1.DateTime, INTERVAL C1.Duration SECOND))
OR
(C1.DateTime >= C2.DateTime
AND
C1.DateTime <= DATE_ADD(C2.DateTime, INTERVAL C2.Duration SECOND) )
OR
(DATE_ADD(C1.DateTime, INTERVAL C1.Duration SECOND) >= C2.DateTime
AND
DATE_ADD(C1.DateTime, INTERVAL C1.Duration SECOND) < DATE_ADD(C2.DateTime, INTERVAL C2.Duration SECOND))
GROUP BY C1.CallId) COverlap
ON COuter.CallId = COverlap.CallId
You would then have to further manipulate the query to partition the results by hour and then to select the maximum for each hour..