Here's a query that I think will give you the results you want. It uses a derived table of the minute values with the minimum difference between them and the nearest multiple of 10 minutes. To differentiate between 9 and 11, we add mn % 10 / 10
, so the results of this query are (e.g. for minutes = 8, 9, 10, 11, 12): 2.8, 1.9, 0, 1.1, 2.2. We discard values > 2 as they should be ignored. This derived table is then JOIN
ed to the table to select the data from the corresponding row:
SELECT d.*
FROM data d
JOIN (SELECT IF(hr = 23 AND mn = 59, dt + INTERVAL 1 DAY, dt) AS date,
IF(mn = 59, (hr + 1) % 24, hr) AS hour,
((mn + 1) DIV 10) % 6 AS mn10,
MIN(LEAST(ABS(mn - mn DIV 10 * 10), ABS(mn - (mn + 9) DIV 10 * 10)) + (mn % 10 / 10)) AS mndiff
FROM data
GROUP BY date, hour, mn10
HAVING mndiff < 2) dd
ON dd.date = IF(d.hr = 23 AND d.mn = 59, d.dt + INTERVAL 1 DAY, d.dt)
AND dd.hour = IF(d.mn = 59, (d.hr + 1) % 24, d.hr)
AND dd.mn10 = ((d.mn + 1) DIV 10) % 6
AND dd.mndiff = LEAST(ABS(d.mn - d.mn DIV 10 * 10), ABS(d.mn - (d.mn + 9) DIV 10 * 10)) + (d.mn % 10 / 10)
Output (for my demo on dbfiddle)
id mn hr dt status
2 50 23 2019-04-19 1
12 1 0 2019-04-20 1
20 11 0 2019-04-20 1
27 19 0 2019-04-20 1
Update
In the case where, for example, data for minutes 9 and 11 are available (but 10 is not) this query will favour the value from minute 11. That can be reversed by changing
+ (mn % 10 / 10)
to:
- (mn % 10 / 10)
and
HAVING mndiff < 2
to:
HAVING mndiff < 1
So the modified query is:
SELECT d.*, dd.*
FROM data d
JOIN (SELECT IF(hr = 23 AND mn = 59, dt + INTERVAL 1 DAY, dt) AS date,
IF(mn = 59, (hr + 1) % 24, hr) AS hour,
((mn + 1) DIV 10) % 6 AS mn10,
MIN(LEAST(ABS(mn - mn DIV 10 * 10), ABS(mn - (mn + 9) DIV 10 * 10)) - (mn % 10 / 10)) AS mndiff
FROM data
GROUP BY date, hour, mn10
HAVING mndiff < 1) dd
ON dd.date = IF(d.hr = 23 AND d.mn = 59, d.dt + INTERVAL 1 DAY, d.dt)
AND dd.hour = IF(d.mn = 59, (d.hr + 1) % 24, d.hr)
AND dd.mn10 = ((d.mn + 1) DIV 10) % 6
AND dd.mndiff = LEAST(ABS(d.mn - d.mn DIV 10 * 10), ABS(d.mn - (d.mn + 9) DIV 10 * 10)) - (d.mn % 10 / 10)
Output for my demo for this query is:
id mn hr dt status
2 50 23 2019-04-19 1
11 59 23 2019-04-19 1
20 11 0 2019-04-20 1
27 19 0 2019-04-20 1
As you can see, it has preferred the 59
value over the 01
value.
Updated dbfiddle
In terms of only selecting values that occur at or before the 10 minute mark (e.g. 10,9,8,7 in order of precedence), you can simplify the query to this:
SELECT d.*
FROM data d
JOIN (SELECT IF(hr = 23 AND mn >= 57, dt + INTERVAL 1 DAY, dt) AS date,
IF(mn >= 57, (hr + 1) % 24, hr) AS hour,
((mn + 3) DIV 10) % 6 AS mn10,
MIN(ABS(mn - (mn + 9) DIV 10 * 10)) AS mndiff
FROM data
GROUP BY date, hour, mn10
HAVING mndiff <= 3) dd
ON dd.date = IF(d.hr = 23 AND d.mn >= 57, d.dt + INTERVAL 1 DAY, d.dt)
AND dd.hour = IF(d.mn >= 57, (d.hr + 1) % 24, d.hr)
AND dd.mn10 = ((d.mn + 3) DIV 10) % 6
AND dd.mndiff = ABS(mn - (mn + 9) DIV 10 * 10)
Output for my demo for this query is:
id mn hr dt status
2 50 23 2019-04-19 1
11 59 23 2019-04-19 1
19 8 0 2019-04-20 1
27 19 0 2019-04-20 1
33 27 0 2019-04-20 1
Updated dbfiddle
Update 2
Based on additional feedback in comments, the time is being displayed as the time of the nearest 10 minute marker rather than the raw time. This results in minor changes to the query:
SELECT dd.mn10 * 10 AS mn, dd.hour, dd.date, d.status, d.id
FROM data d
JOIN (SELECT IF(hr = 23 AND mn >= 57, dt + INTERVAL 1 DAY, dt) AS date,
IF(mn >= 57, (hr + 1) % 24, hr) AS hour,
((mn + 3) DIV 10) % 6 AS mn10,
MIN(ABS(mn - (mn + 9) DIV 10 * 10)) AS mndiff
FROM data
GROUP BY date, hour, mn10
HAVING mndiff <= 3) dd
ON dd.date = IF(d.hr = 23 AND d.mn >= 57, d.dt + INTERVAL 1 DAY, d.dt)
AND dd.hour = IF(d.mn >= 57, (d.hr + 1) % 24, d.hr)
AND dd.mn10 = ((d.mn + 3) DIV 10) % 6
AND dd.mndiff = ABS(mn - (mn + 9) DIV 10 * 10)
ORDER BY dd.date, dd.hour, mn
Output from updated demo
mn hour date status id
0 23 2019-04-19 1 12
50 23 2019-04-19 1 2
0 0 2019-04-20 1 11
10 0 2019-04-20 1 20
20 0 2019-04-20 1 28
30 0 2019-04-20 1 34