Overriding Data based on sub query - mysql

I have a Rates table that records the rate of a process
DateTime Rate
2013-11-25 05:00:00 22
2013-11-25 06:00:00 78
2013-11-25 07:00:00 33
2013-11-25 07:10:00 56
2013-11-25 08:30:00 12
and a Downtime table that records time periods where the above data may not be valid
StartDateTime EndDateTime
2013-11-25 04:59:00 2013-11-25 05:10:00
2013-11-25 07:00:00 2013-11-25 07:15:00
How can I get the following output where any Rate value recorded between any period in the Downtime table is replaced by a fixed value e.g. 50?
DateTime Rate
2013-11-25 05:00:00 50
2013-11-25 06:00:00 78
2013-11-25 07:00:00 50
2013-11-25 07:10:00 50
2013-11-25 08:30:00 12

This should do the trick:
SELECT r.datetime, if(d.startDatetime IS NULL, r.rate, 50) rate
FROM rates r
LEFT JOIN downtime d
ON r.datetime BETWEEN d.startDatetime AND d.endDatetime
Fiddle here.

Related

Mysql datetime count the hours worked between a specific time

I'm looking for a way to count the hours worked between a given time range.
For example to count from the MySQL data below the hours worked between 22:00 and 06:00.
Using date_start 2022-04-01 21:00:00 and date_end 2022-04-02 08:00:00 the user worked 11 hours total and 8 night hours.
Of course the data could also be something like 2022-04-01 05:00:00 and 2022-04-01 16:00:00 which will then need to output 2 night hours or 2022-04-01 18:00:00and 2022-04-02 03:00:00 which outputs 5 night hours.
MySQL table:
CREATE TABLE `tasks` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`date_start` datetime DEFAULT NULL,
`date_end` datetime DEFAULT NULL,
UNIQUE KEY `id_UNIQUE` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
INSERT INTO `tasks` (`date_start`,`date_end`) VALUES
('2022-04-01 04:00:00', '2022-04-01 16:00:00'), # 2:00 nighthours
('2022-04-02 05:00:00', '2022-04-02 23:30:00'), # 2:30 nighthours
('2022-04-03 06:00:00', '2022-04-03 18:00:00'), # 0:00 nighthours
('2022-04-04 12:00:00', '2022-04-05 00:00:00'), # 2:00 nighthours
('2022-04-05 19:00:00', '2022-04-06 07:00:00'); # 8:00 nighthours
Current MySQL:
# 21600 = 06:00 hours
# 79200 = 22:00 hours
SELECT t.date_start, t.date_end, DATE_FORMAT(TIMEDIFF(
(CASE WHEN DATE(t.date_start) != DATE(t.date_end) AND TIME_TO_SEC(t.date_end) > 21600 THEN DATE_FORMAT(t.date_end, '%Y-%m-%d 06:%i:%s')
WHEN TIME_TO_SEC(t.date_start) < 21600 THEN DATE_FORMAT(t.date_start, '%Y-%m-%d 06:%i:%s')
ELSE t.date_end END),
(CASE WHEN DATE(t.date_start) != DATE(t.date_end) AND TIME_TO_SEC(t.date_start) < 79200 THEN DATE_FORMAT(t.date_start, '%Y-%m-%d 22:%i:%s')
WHEN TIME_TO_SEC(t.date_end) > 79200 THEN DATE_FORMAT(t.date_start, '%Y-%m-%d 22:%i:%s')
WHEN DATE(t.date_start) = DATE(t.date_end) AND TIME_TO_SEC(t.date_end) <= 79200 AND TIME_TO_SEC(t.date_start) >= 21600 THEN t.date_end
ELSE t.date_start END)
), '%H:%i') AS night_time FROM tasks t;
Currently I still have a problem in my current MySQL when the start_date and end_date both start on the same day and both have night hours. for example 2022-04-02 05:00:00 and 2022-04-02 23:30:00 which has 01:00 night hour in start_date and 1:30 hour in end_date (total night: 02:30 hours)
I am not sure if my current MySQL is the best/fastest way to achieve my goal.
Calculating Time Overlaps
You can calculate the amount of time two date ranges overlap using:
MIN( EndDate1, EndDate2 ) - MAX( StartDate1, StartDate2 )
For example if the date ranges are:
Date_Start
Date_End
Night_Shift_Start
Night_Shift_End
2022-04-01 21:00:00
2022-04-02 08:00:00
2022-04-01 22:00:00 **
2022-04-02 06:00:00 **
The result would be 8 hours:
Min( EndDate ) - Max( StartDate )
.... As Unix Timestamps
Time Overlap
2022-04-02 06:00:00 (minus) 2022-04-01 22:00:00
1648875600 - 1648846800 = 28800 seconds
08:00:00 hours
Checking for Multiple Overlaps
Since technically a single shift could have both started and ended during "night hours" (22:00 to 06:00) you need check for overlaps on both sides.
Date_Start
Date_End
Night Hours
...
2022-04-05 05:00:00
2022-04-05 23:30:00
2.5 hours
(1 hour) : 2022-04-05 05:00 to 2022-04-05 06:00 (1.5 hours) : 2022-04-05 22:00 to 2022-04-05 23:30
One approach is using the base start/end times to calculate the previous and upcoming "night hour" periods:
SELECT *
, TIMESTAMP(DATE(date_start) - INTERVAL 1 DAY, '22:00:00') AS current_start
, TIMESTAMP(DATE(date_start), '06:00:00') AS current_end
, TIMESTAMP(DATE(date_start), '22:00:00') AS next_start
, TIMESTAMP(DATE(date_start) + INTERVAL 1 DAY, '06:00:00') AS next_end
FROM tasks
Results:
id
date_start
date_end
current_start
current_end
next_start
next_end
1
2022-04-01 04:00:00
2022-04-01 16:00:00
2022-03-31 22:00:00
2022-04-01 06:00:00
2022-04-01 22:00:00
2022-04-02 06:00:00
2
2022-04-02 05:00:00
2022-04-02 23:30:00
2022-04-01 22:00:00
2022-04-02 06:00:00
2022-04-02 22:00:00
2022-04-03 06:00:00
3
2022-04-03 06:00:00
2022-04-03 18:00:00
2022-04-02 22:00:00
2022-04-03 06:00:00
2022-04-03 22:00:00
2022-04-04 06:00:00
4
2022-04-04 12:00:00
2022-04-05 00:00:00
2022-04-03 22:00:00
2022-04-04 06:00:00
2022-04-04 22:00:00
2022-04-05 06:00:00
5
2022-04-05 19:00:00
2022-04-06 07:00:00
2022-04-04 22:00:00
2022-04-05 06:00:00
2022-04-05 22:00:00
2022-04-06 06:00:00
6
2022-04-01 04:00:00
2022-04-01 16:00:00
2022-03-31 22:00:00
2022-04-01 06:00:00
2022-04-01 22:00:00
2022-04-02 06:00:00
7
2022-04-05 19:00:00
2022-04-06 07:00:00
2022-04-04 22:00:00
2022-04-05 06:00:00
2022-04-05 22:00:00
2022-04-06 06:00:00
8
2022-04-05 05:00:00
2022-04-05 23:30:00
2022-04-04 22:00:00
2022-04-05 06:00:00
2022-04-05 22:00:00
2022-04-06 06:00:00
Total Overlap Time
Once you have the "night hour" ranges, calculate the overlapping time on both sides and add them together to get the total time worked during "night hours"
SELECT id
, date_start
, date_end
, SEC_TO_TIME(
GREATEST(0, start_overlap__in_seconds) -- ignore negative time, which means no overlap
+ GREATEST(0, end_overlap_in_seconds)
) AS time_overall
FROM (
SELECT *
, UNIX_TIMESTAMP(LEAST(date_end, current_end))
- UNIX_TIMESTAMP(GREATEST(date_start, current_start))
AS start_overlap__in_seconds
, UNIX_TIMESTAMP(LEAST(date_end,next_end))
- UNIX_TIMESTAMP(GREATEST(date_start,next_start))
AS end_overlap_in_seconds
FROM (
SELECT *
, TIMESTAMP(DATE(date_start) - INTERVAL 1 DAY, '22:00:00') AS current_start
, TIMESTAMP(DATE(date_start), '06:00:00') AS current_end
, TIMESTAMP(DATE(date_start), '22:00:00') AS next_start
, TIMESTAMP(DATE(date_start) + INTERVAL 1 DAY, '06:00:00') AS next_end
FROM tasks
) tmp
) t
Final Results:
id
date_start
date_end
time_overall
1
2022-04-01 04:00:00
2022-04-01 16:00:00
02:00:00
2
2022-04-02 05:00:00
2022-04-02 23:30:00
02:30:00
3
2022-04-03 06:00:00
2022-04-03 18:00:00
00:00:00
4
2022-04-04 12:00:00
2022-04-05 00:00:00
02:00:00
5
2022-04-05 19:00:00
2022-04-06 07:00:00
08:00:00
6
2022-04-01 04:00:00
2022-04-01 16:00:00
02:00:00
7
2022-04-05 19:00:00
2022-04-06 07:00:00
08:00:00
8
2022-04-05 05:00:00
2022-04-05 23:30:00
02:30:00
db<>fiddle here

Diff between values in different rows, based on timediff

MariaDB version 10.4.10.
I have a stock scraper script that fetches stock data every hour and inserts it into a MySQL database. I want a way to get price difference for each stock between, for example:
stocks fetched at 2020-03-25 07:00 and 2020-03-25 19:00 (12 hours)
stocks fetched at 2020-03-25 07:00 and 2020-03-26 07:00 (24 hours)
stocks fetched at 2020-03-25 08:00 and 2020-03-25 20:00 (12 hours)
stocks fetched at 2020-03-25 08:00 and 2020-03-26 08:00 (24 hours)
etc
The database structure looks something like this:
stocks( time_fetched DATETIME, name VARCHAR, price INT )
Some sample data:
**time_fetched name price**
2020-03-25 07:00:00 stock_A 10
2020-03-25 07:00:00 stock_B 14
2020-03-25 08:00:00 stock_A 12
2020-03-25 08:00:00 stock_B 20
...
2020-03-25 19:00:00 stock_A 28
2020-03-25 19:00:00 stock_B 32
2020-03-25 20:00:00 stock_A 40
2020-03-25 20:00:00 stock_B 36
...
2020-03-26 07:00:00 stock_A 12
2020-03-26 07:00:00 stock_B 16
2020-03-26 08:00:00 stock_A 18
2020-03-26 08:00:00 stock_B 16
Expected result:
**time_fetched name current_price price_12h_ago price_24h_ago**
2020-03-25 19:00:00 stock_A 28 10 NULL
2020-03-25 19:00:00 stock_B 32 14 NULL
2020-03-25 20:00:00 stock_A 40 12 NULL
2020-03-25 20:00:00 stock_B 36 20 NULL
2020-03-26 07:00:00 stock_A 12 28 10
2020-03-26 07:00:00 stock_B 16 32 14
2020-03-26 08:00:00 stock_A 18 40 12
2020-03-26 08:00:00 stock_B 16 36 20
Currently I am using SQL similar to this:
WITH prices AS (
SELECT time_fetched, name, price,
LAG(price, 12) OVER(PARTITION BY name ORDER BY time_fetched) AS price_12h_ago,
LAG(price, 24) OVER(PARTITION BY name ORDER BY time_fetched) AS price_24h_ago
FROM stocks
)
SELECT time_fetched, name, price AS current_price, price_12h_ago, price_24h_ago
FROM prices
This works, given that all stocks have price data fetched for all hours. In reality, there are sometimes gaps beetween hours, and price data for some hours and some stocks are missing in the stocks database.
This means that the above code that fetches price on 12 rows before the current one, does not always represent 12 hours before current row.
So I would need a way to get price difference based on actual timediff.
Hope this makes any sense to anyone out there :)
You can use the range() clause. If your times are precise:
SELECT time_fetched, name, price,
MIN(price) OVER (PARTITION BY name
ORDER BY time_fetched
RANGE BETWEEN INTERVAL 12 hour AND INTERVAL 12 hour
) as price_12h_ago,
MIN(price) OVER (PARTITION BY name
ORDER BY time_fetched
RANGE BETWEEN INTERVAL 24 hour AND INTERVAL 24 hour
) as price_24h_ago
FROM stocks;
Unless you set the minutes and seconds to exactly 0, you may want a broader range than just an instant. For instance:
SELECT time_fetched, name, price,
MIN(price) OVER (PARTITION BY name
ORDER BY time_fetched
RANGE BETWEEN INTERVAL '12:05' HOUR_MINUTE AND INTERVAL '11:55' HOUR_MINUTE
) as price_12h_ago,
MIN(price) OVER (PARTITION BY name
ORDER BY time_fetched
RANGE BETWEEN INTERVAL '24:05' HOUR_MINUTE AND INTERVAL '23:55' HOUR_MINUTE
) as price_24h_ago
FROM stocks;

MySQL SELECT query with timetable

I have trouble creating Mysql query that will give me a list of id depending on the current time and timetable specified. I have a table like so:
ID - int
0f - time (monday from)
0t - time (monday till)
1f - time (tuesday from)
1t - time (tuesday till)
2f - time (wednesday from)
2t - time (wednesday till)
3f - time (thursday from)
3t - time (thursday till)
4f - time (friday from)
4t - time (friday till)
5f - time (saturday from)
5t - time (saturday till)
6f - time (sunday from)
6t - time (sunday till)
The problem starts when "till" value is grater than 24 hour and becomes lower then "from" value.
For example, it is 1am and I need to select all ids that work now and one works from 10am till 2am.
UPD That is an example of my table with real data:
ID Name f0 t0 f1 t1 f2 t2 f3 t3 f4 t4 f5 t5 f6 t6
23 test1 10:00:00 23:00:00 10:00:00 23:00:00 10:00:00 23:00:00 10:00:00 23:00:00 10:00:00 01:00:00 11:00:00 01:00:00 11:00:00 23:00:00
24 test2 12:00:00 01:00:00 12:00:00 01:00:00 12:00:00 01:00:00 12:00:00 01:00:00 12:00:00 02:00:00 12:00:00 02:00:00 12:00:00 01:00:00
25 test3 12:00:00 01:00:00 12:00:00 01:00:00 12:00:00 01:00:00 12:00:00 01:00:00 12:00:00 02:00:00 12:00:00 02:00:00 12:00:00 01:00:00
27 test4 11:00:00 23:00:00 11:00:00 23:00:00 11:00:00 23:00:00 11:00:00 23:00:00 11:00:00 23:00:00 11:00:00 23:00:00 11:00:00 23:00:00
28 test5 10:00:00 23:00:00 10:00:00 23:00:00 10:00:00 23:00:00 10:00:00 23:00:00 10:00:00 23:00:00 10:00:00 23:00:00 10:00:00 23:00:00
29 test6 09:00:00 22:00:00 09:00:00 22:00:00 09:00:00 22:00:00 09:00:00 22:00:00 09:00:00 22:00:00 10:00:00 23:00:00 10:00:00 23:00:00
30 test7 12:00:00 01:00:00 12:00:00 01:00:00 12:00:00 01:00:00 12:00:00 01:00:00 12:00:00 03:00:00 12:00:00 03:00:00 12:00:00 01:00:00
32 test8 09:00:00 22:00:00 09:00:00 22:00:00 09:00:00 22:00:00 09:00:00 22:00:00 09:00:00 22:00:00 10:00:00 22:00:00 10:00:00 22:00:00
33 test9 11:00:00 22:00:00 11:00:00 22:00:00 11:00:00 22:00:00 11:00:00 22:00:00 11:00:00 22:00:00 11:00:00 22:00:00 11:00:00 22:00:00
34 test10 09:00:00 22:00:00 09:00:00 22:00:00 09:00:00 22:00:00 09:00:00 22:00:00 09:00:00 22:00:00 09:00:00 22:00:00 09:00:00 22:00:00
35 test11 18:00:00 02:00:00 18:00:00 02:00:00 18:00:00 02:00:00 18:00:00 02:00:00 18:00:00 04:00:00 18:00:00 04:00:00 18:00:00 02:00:00
AND I need to write MYSQL query to select ID's that are open now at any time
I'm not completely sure what you're asking for here. So I will offer a couple of observations.
First, given any DATE, TIMESTAMP, or DATETIME value, for example a column with the name datestamp, this formula will give you precisely midnight of the Monday of that week. (There's a little bit of magic in this formula, because MySQL's DAY 0 happens to be a Sunday.)
FROM_DAYS(TO_DAYS(datestamp) -MOD(TO_DAYS(datestamp) -2, 7))
Then, you can add an arbitrary number of hours to that. So, for example, if you have an eight-hour work shift that begins at 23:00 on Wednesday, it begins 71 (24 + 24 + 23) hours after the beginning of the present week. So it begins at
FROM_DAYS(TO_DAYS(NOW()) -MOD(TO_DAYS(NOW()) -2, 7))
+ INTERVAL 71 HOUR
The end of that shift in the present week, eight hours later, is at this time.
FROM_DAYS(TO_DAYS(NOW()) -MOD(TO_DAYS(NOW()) -2, 7))
+ INTERVAL 71 HOUR
+ INTERVAL 8 HOUR
So, maybe (???) you want to represent your "Thursday till" sorts of items as hours after the beginning of the week.
This is just an idea for a way to represent recurring weekly events in your dbms, while still getting the advantages of MySQL's date arithmetic.

Get 30 minutes interval data between start and end time in mysql

I have table structure in mysql,
table_id no_people booking_date bookingend_time bookingstart_time
14 2 2014-10-31 2014-10-31 13:30:00 2014-10-31 11:00:00
5 4 2014-10-31 2014-10-31 16:30:00 2014-10-31 14:30:00
6 2 2014-10-31 2014-10-31 17:00:00 2014-10-31 16:00:00
2 4 2014-11-06 2014-11-06 12:30:00 2014-11-06 10:00:00
2 4 2014-10-31 2014-10-31 16:00:00 2014-10-31 14:00:00
3 4 2014-11-01 2014-11-01 09:00:00 2014-11-01 07:30:00
6 2 2014-11-01 2014-11-01 10:00:00 2014-11-01 07:30:00
2 4 2014-11-03 2014-11-03 10:30:00 2014-11-03 08:30:00
5 4 2014-11-04 2014-11-04 10:30:00 2014-11-04 08:30:00
3 4 2014-11-05 2014-11-05 09:30:00 2014-11-05 07:30:00
14 2 2014-11-05 2014-11-05 09:30:00 2014-11-05 07:30:00
I want to retrieve table_id data with 30 minutes of interval between start and end time.
Ex:
if i give booking start time 10:30 and end time 12:30 i should get 14 as row..
Similarly it should check all rows and return between two times ..
My query so far
SELECT `table_id` FROM `booking` WHERE bookingstart_time>='2014-10-31 10:30:00' AND bookingend_time<='2014-10-31 11:30:00'
Step 1: expand the input time frame by 30 minutes before and 30 minutes after. DATE_ADD() and DATE_SUB() can do that:
DATE_SUB(_input_start_date_here_, INTERVAL 30 MINUTE)
Step 2: rethink your problem in terms of start and end times. Here are the possible cases:
if the booking started during the (expanded) period, then you want this booking in your result
or if the booking started before the period, then you want this booking unless it also ended before the period
on the other hand, if the booking started after the period, then you do not want this booking
The first situation above could be expressed like this:
WHERE bookingstart_time >= DATE_SUB(_input_start_date_here_, INTERVAL 30 MINUTE)
AND bookingstart_time <= DATE_ADD(_input_end_date_here_, INTERVAL 30 MINUTE)
The second condition is left as an exercise. You can also rewrite the above with a more elegant BETWEEN operator.
SELECT restaurant_table FROM rest_restaurantbooking WHERE TIMESTAMPDIFF(SECOND, bookingstart_time, bookingend_time) > 1800.
FOR REFERENCE: HERE

MySQL Count Numbers Are Off

I am not sure why my numbers are drastically off from each other.
A query with no max id:
SELECT id, DATE_FORMAT(t_stamp, '%Y-%m-%d %H:00:00') as date, COUNT(*) as count
FROM test_ips
WHERE id > 0
AND viewip != ""
GROUP BY HOUR(t_stamp)
ORDER BY t_stamp ASC;
I get:
1 2012-07-18 19:00:00 1313
106 2012-07-18 20:00:00 1567
107 2012-07-19 09:00:00 847
225 2012-07-19 10:00:00 5095
421 2012-07-19 11:00:00 205
423 2012-07-19 12:00:00 900
461 2012-07-19 13:00:00 619
490 2012-07-20 15:00:00 729
575 2012-07-20 16:00:00 1682
1060 2012-07-20 17:00:00 2063
2260 2012-07-20 18:00:00 1417
5859 2012-07-20 21:00:00 1303
7060 2012-07-20 22:00:00 1340
8280 2012-07-20 23:00:00 1211
9149 2012-07-21 00:00:00 1675
10418 2012-07-21 01:00:00 721
11127 2012-07-21 02:00:00 825
But if I add a max id:
AND id <= 8279
I get:
1 2012-07-18 19:00:00 1313
106 2012-07-18 20:00:00 1201
107 2012-07-19 09:00:00 118
225 2012-07-19 10:00:00 196
421 2012-07-19 11:00:00 2
423 2012-07-19 12:00:00 38
461 2012-07-19 13:00:00 20
490 2012-07-20 15:00:00 85
575 2012-07-20 16:00:00 483
1060 2012-07-20 17:00:00 1200
2260 2012-07-20 18:00:00 1200
5859 2012-07-20 21:00:00 1201
7060 2012-07-20 22:00:00 1220
The numbers are WAY off from each other. Something is goofy.
EDIT: Here is my table structure:
id t_stamp bID viewip unique
1 2012-07-18 19:22:20 5 192.168.1.1 1
2 2012-07-18 19:22:21 1 192.168.1.1 1
3 2012-07-18 19:22:22 5 192.168.1.1 0
4 2012-07-18 19:22:22 3 192.168.1.1 1
You are not grouping by ID and I think you intend to.
Try:
SELECT id, DATE_FORMAT(t_stamp, '%Y-%m-%d %H:00:00') as date, COUNT(*) as count
FROM test_ips
WHERE id > 0
AND viewip != ""
GROUP BY id, DATE_FORMAT(t_stamp, '%Y-%m-%d %H:00:00')
ORDER BY t_stamp;
Your query is not consistent.
In your select statement you are displaying the full date.
But you are grouping your data by the hour. So your count statement is taking the count of all the data for each hour of the day.
As an example take your first result:
1 2012-07-18 19:00:00 1313
The count of 1313 contains the records for all of your dates (7/18, 7/19, 7/20, 7/21, 7/22, etc) that have an hour of 19:00.
But the way you have your query setup, it looks like it should be the count of all records for 2012-07-18 19:00:00.
So when you add AND id <= 8279" The dates of 7/21 and some of 7/20 or no longer being counted so your count values are now lower.
I'm guessing you are meaning to group by the date and hour and not just the hour.