Compute based on the value of time in mysql - mysql

I'm trying to compute a column based on the value of time, i'm not exactly sure what's better to use if I should use an If condition or Case?
Here are my columns
| Time_In | Time_Out | Val1 | Val2 |
| 2014-07-19 04:00:04 | 2014-07-19 08:00:00 | 5 | 15 |
What i'm trying to do is if the time is if the time is between '08:00:00' to '17:00:00' (business hours) it will automatically multiply Val1 and Val2
If the time is '18:00:00' to '23:00:00' (after business hours)
The only step I'm able to get right now is the getting the time in the timestamp cause that's what i'll be comparing
`SELECT date_format(STR_TO_DATE(Time_In, '%Y-%m-%d %H:%i:%s'), '%H:%i:%s')
Output: 04:00:04
`SELECT date_format(STR_TO_DATE(Time_Out, '%Y-%m-%d %H:%i:%s'), '%H:%i:%s')
Output: 08:00:00
How should I construct this?
Thank You for the help
I tried it using the if condition first and this is working :)
SELECT
IF(`Time_In` >= CAST('13:00:01' AS time) AND `Time_Out` <= CAST('15:00:00' AS time),(Val11 * Val2),'false')
FROM table WHERE ID = '5'
And got the output 75 :D

As you assumed you can do that with a CASE construct.
convert your string to a DATETIME value with STR_TO_DATE
extract the TIME part with the TIME function
So you could do it, here with to example values of
'2014-07-19 12:00:00'
'2014-07-19 18:00:00'
with this statement (you would need only one CASE construct of course):
SELECT
CASE
WHEN Time(STR_TO_DATE('2014-07-19 12:00:00', '%Y-%m-%d %H:%i:%s')) BETWEEN '04:00:00' AND '17:00:00'
THEN Val1 * Val2
ELSE NULL
END result,
CASE
WHEN Time(STR_TO_DATE('2014-07-19 18:00:00', '%Y-%m-%d %H:%i:%s')) BETWEEN '04:00:00' AND '17:00:00'
THEN Val1 * Val2
ELSE NULL
END result2
FROM
example;
DEMO

Related

MY SQL does not pull data past midnight UTC

I am really having issue showing the items. I have two different times(they are in UTC).
FOR Eg: startTime - 15:00:00(in UTC) and cutOffTime - 03:00:00(in UTC and supposed to be up to 3am UTC next morning). My local timezone is EST. The issue I am having is after 8pm(EST) when next day start at UTC. My items does not shows up. Here is my sql query. I am pulling startTime and cutOffTime from table2. MY server is in UTC as well.
SELECT a.id, a.column1, a.catID, b.startTime, b.cutOffTime, IFNULL(b.`column2`, 'everyday') as 'availableDays'
FROM table1 a
RIGHT JOIN table2 b on a.catID = b.catID
WHERE id = 012
and CASE WHEN b.startTime IS NOT NULL THEN CONCAT(CURDATE(),' ', b.startTime) < NOW() ELSE 1 END
and CASE WHEN b.cutOffTime IS NOT NULL THEN If((b.startTime is not NULL AND b.cutOffTime < b.startTime), DATE_ADD(CONCAT(CURDATE(),' ', b.cutOffTime), INTERVAL 24 HOUR), CONCAT(CURDATE(),' ', b.cutOffTime)) > NOW() ELSE 1 END
HAVING availableDays in ('everyday', 'wednesday')
Order by a.column1 ASC;
My table2 is like this:
catID Cat startTime cutOffTime
1. Test 15:00:00 03:00:00
2. Test2 null null
3. Test3 15:00:00 null
Suppose it is 8:30pm EST(which is 00:30:00 UTC) then I am expecting to get all the results. Now, It is only returning me 2 and 3.

Amount of time for giving the date

I'm trying to get a report in MySQL for consecutive days based on activity recorded. I have the start date&time and end date&time of a given status. My goal is to receive a report in a form:
Status|Date|Sum of activity
The problem that I've encountered is that some activities start i.e. 2019-12-12 18:21:12 and ends the next day 2019-12-13 03:21:12. Is there a way to let's say split the result for one date until 23:59:59 and add the rest of time to the following day? So far I have a code below, but it just sums the timestampdiff.
USE db;
SELECT
table1.status,
left(table1.start_time, 7) ' Date',
sec_to_time(
sum(
timestampdiff(
second,
table1.start_time,
(
case when table1.end_time is null then now() else table1.end_time end
)
)
)
) 'Sum of activity'
FROM
table1
GROUP by 1,2
Update : Let me clarify a bit my question. I have some activities that take for example 36 hours, starting on 2019-12-20 and ending on 2019-12-22. I need a composed monthly report with each day in the month selected from start_time, so for the example described above (36h over 3 days) I would like to get:
Activity1|2019-12-20|3h
Activity1|2019-12-21|24h
Activity1|2019-12-22|9h
Update2: Thank you for the 2nd update,but the proposed code works only for the first record in the dataset (for more records the time is not summed up) and doesn't take into account the activity type. I will provide more data maybe it will help:
Activity start_time end_time
1048 2019-12-27 06:42:51 2019-12-27 07:11:42
1048 2019-12-29 07:07:11 2019-12-29 07:08:59
1048 2019-12-29 07:09:19 2019-12-29 07:21:10
2066 2019-12-25 07:08:00 2019-12-25 19:01:17
2066 2019-12-25 19:01:17 2019-12-26 06:55:15
2066 2019-12-26 06:55:15 2019-12-26 18:20:51
You can use date() function :
select status, date(start_time) as date, count(*) as "Sum of activities"
from table1
group by status, date(start_time);
Demo
Update (depending on your comment): Try to use
select status, date(start_time) as date,
sec_to_time(sum(timestampdiff(second,
start_time,
(case
when end_time is null then
now()
else
end_time
end))))
as "Sum of activities"
from table1
group by status, date(start_time);
Update2 : To accomplish the last mentioned duty, need to generate rows firstly :
select date1,
extract( hour from
sec_to_time(
sum(case
when date1 = date(start_time) then
timestampdiff(second,start_time,date2)
when date1 = date(end_time) then
timestampdiff(second,date1,end_time)
else
timestampdiff(second,date1,date2)
end
)) ) as "Time Difference as hour"
from
(
select #cr := #cr + 1 as rn,
date_sub(date(end_time), interval date(end_time)-date(start_time) - #cr + 1 day) as date1,
date_sub(date(end_time), interval date(end_time)-date(start_time) - #cr day) as date2,
start_time, end_time
from information_schema.tables c1
cross join ( select #cr := 0 ) r
cross join table1 t
where #cr < date(end_time)- date(start_time)+1
) q
group by date1;
Demo 2
removing extract( hour from ) part you can get the whole difference upto second precision.

mysql LAST_DAY() only reads 1 subquery result, how to process all results? using joins?

I have an insurance policies table like this:
+-------------------------------------------------------------+
| id | cancellation_val | cancellation_interval | expire_date |
+-------------------------------------------------------------+
| 1 | 30 | day | 2019-06-09 |
| 2 | 2 | month | 2019-12-01 |
+-------------------------------------------------------------+
I need to get the ids of the policies that are going to expire based on cancellation, from today and within 4 months, calculating the last day of the month, like this pseudo-code:
'today' <= LAST_DAY( expire_date - cancellation_val/interval ) < 'today + 4 months'
Being not a pro I think I should use JOINs but I don't know how, after days of trying the only thing I achieved was this:
SELECT LAST_DAY(
DATE_FORMAT(
STR_TO_DATE(
(SELECT CASE cancellation_interval
WHEN "day" THEN date_sub(expire_date, INTERVAL cancellation_val DAY)
WHEN "month" THEN date_sub(data_scadenzaexpire_date, INTERVAL cancellation_val MONTH)
END
AS newDate
FROM insurance WHERE id=2
), '%Y-%m-%d'
), '%Y-%m-%d'
)
)
This is working but I don't need the "WHERE id=2" clause (because I need to process ALL rows of the table), and if I remove it I got error "subquery returns more than 1 row".
So how I can proceed? And using the result to stay between 'today' AND 'today + 4 months' ?
I think with some kind of JOIN I could do it in a easier way but I don't know how.
Thank you all
The problem is the structure of the query, not the LAST_DAY function.
We want to return the id values of rows that meet some condition. So the query would be of the form:
SELECT t.id
, ...
FROM insurance t
WHERE ...
HAVING ...
Introducing another SELECT keyword basically introduces a subquery. There are restrictions on subqueries... in the SELECT list, a subquery can return a single column and (at most) a single row.
So let's ditch that extra SELECT keyword.
We can derive the newdate as an expression of the SELECT list, and then we can reference that derived column in the HAVING clause. The spec said we wanted to return the id value, so we include that in the SELECT list. We don't have to return any other columns, but for testing/debugging, it can be useful to return the values that were used to derive the newdate column.
Something like this:
SELECT t.id
, LAST_DAY(
CASE t.cancellation_interval
WHEN 'day' THEN t.expire_date - INTERVAL t.cancellation_val DAY
WHEN 'month' THEN t.expire_date - INTERVAL t.cancellation_val MONTH
ELSE t.expire_date
END
) AS newdate
, t.expire_date
, t.cancellation_interval
, t.cancellation_val
FROM insurance t
HAVING newdate >= DATE(NOW())
AND newdate <= DATE(NOW()) + INTERVAL 4 MONTH
ORDER
BY newdate ASC
We don't have to include the newdate in the SELECT list; we could just replace occurrences of newdate in the HAVING clause with the expression.
We could also use an inline view to "hide" the derivation of the newdate column
SELECT v.id
, v.newdate
FROM ( SELECT t.id
, LAST_DAY(
CASE t.cancellation_interval
WHEN 'day' THEN t.expire_date - INTERVAL t.cancellation_val DAY
WHEN 'month' THEN t.expire_date - INTERVAL t.cancellation_val MONTH
ELSE t.expire_date
END
) AS newdate
FROM insurance t
) v
WHERE v.newdate >= DATE(NOW())
AND v.newdate <= DATE(NOW()) + INTERVAL 4 MONTH
ORDER
BY v.newdate ASC
check this query: remove the HAVING Line to see all rows
SELECT
IF(cancellation_interval = 'day',
i.expire_date - INTERVAL i.`cancellation_val` DAY,
i.expire_date - INTERVAL i.`cancellation_val` MONTH
) as cancellation_day,
i.*
FROM `insurance` i
HAVING cancellation_day < NOW() + INTERVAL 4 MONTH;
SAMPLES
MariaDB [test]> SELECT IF(cancellation_interval = 'day', i.expire_date - INTERVAL i.`cancellation_val` DAY, i.expire_date - INTERVAL i.`cancellation_val` MONTH ) as cancellation_day, i.* FROM `insurance` i HAVING cancellation_day < NOW() + INTERVAL 4 MONTH;
+------------------+----+------------------+-----------------------+-------------+
| cancellation_day | id | cancellation_val | cancellation_interval | expire_date |
+------------------+----+------------------+-----------------------+-------------+
| 2019-05-10 | 1 | 30 | day | 2019-06-09 |
+------------------+----+------------------+-----------------------+-------------+
1 row in set (0.001 sec)
When you use a SELECT query as an expression, it can only return one row.
If you want to process all the rows, you need to call LAST_DAY() inside the query, not on the result.
SELECT *
FROM insurance
WHERE CURDATE() <= LAST_DAY(
expire_date - IF(cancellation_interval = 'day',
INTERVAL cancellation_val DAY,
INTERVAL cancellation_val MONTH))
AND LAST_DAY(expire_date - IF(cancellation_interval = 'day',
INTERVAL cancellation_val DAY,
INTERVAL cancellation_val MONTH)) < CURDATE + INTERVAL 4 MONTH

Add and Sub time in MySQL

In MySQL table I have these value 00:00:00 and 06:00:00.
I need sub three hours on 00:00:00, return 21:00:00 and add three hours on 06:00:00, return 09:00:00.
I have tried this sql query, without success.
Please help me, thank you in advance.
mysql> SELECT
SUBTIME('00:00:00', 3),
ADDTIME('06:00:00', 3);
+------------------------+------------------------+
| SUBTIME('00:00:00', 3) | ADDTIME('06:00:00', 3) |
+------------------------+------------------------+
| -00:00:03 | 06:00:03 |
+------------------------+------------------------+
1 row in set
mysql>
#Edit 1
mysql> SELECT
'00:00:00' - INTERVAL 3 HOUR,
'06:00:00' + INTERVAL 3 HOUR;
+------------------------------+------------------------------+
| '00:00:00' - INTERVAL 3 HOUR | '06:00:00' + INTERVAL 3 HOUR |
+------------------------------+------------------------------+
| NULL | NULL |
+------------------------------+------------------------------+
1 row in set
mysql>
#Edit 2
mysql> SELECT
DATE_ADD('00:00:00', INTERVAL 3 HOUR),
DATE_ADD('06:00:00', INTERVAL 3 HOUR);
SELECT
SUBTIME('00:00:00', '03:00:00'),
ADDTIME('06:00:00', '03:00:00');
+---------------------------------------+---------------------------------------+
| DATE_ADD('00:00:00', INTERVAL 3 HOUR) | DATE_ADD('06:00:00', INTERVAL 3 HOUR) |
+---------------------------------------+---------------------------------------+
| NULL | NULL |
+---------------------------------------+---------------------------------------+
1 row in set
+---------------------------------+---------------------------------+
| SUBTIME('00:00:00', '03:00:00') | ADDTIME('06:00:00', '03:00:00') |
+---------------------------------+---------------------------------+
| -03:00:00 | 09:00:00 |
+---------------------------------+---------------------------------+
1 row in set
mysql>
Since there is actually no time like '-00:00:03', you cannot apply time operation to do such thing.
If you really need to to this, I recommend you to
Change the column to INT type, and do the operation by yourself.
Change the column to DATETIME type, and try to deal with the cross-day problem by yourself.
Or apply the following...
SELECT CONCAT(
TIMESTAMPDIFF(
HOUR,
STR_TO_DATE(CONCAT('2015/01/01 ', '00:00:00'), '%Y/%m/%d %H:%i:%s'),
STR_TO_DATE(CONCAT('2015/01/01 ', '00:00:00'), '%Y/%m/%d %H:%i:%s') - interval 3 hour
) % 24,
":",
TIMESTAMPDIFF(
MINUTE,
STR_TO_DATE(CONCAT('2015/01/01 ', '00:00:00'), '%Y/%m/%d %H:%i:%s'),
STR_TO_DATE(CONCAT('2015/01/01 ', '00:00:00'), '%Y/%m/%d %H:%i:%s') - interval 3 hour
) % 60,
":",
TIMESTAMPDIFF(
SECOND,
STR_TO_DATE(CONCAT('2015/01/01 ', '00:00:00'), '%Y/%m/%d %H:%i:%s'),
STR_TO_DATE(CONCAT('2015/01/01 ', '00:00:00'), '%Y/%m/%d %H:%i:%s') - interval 3 hour
) % 60
);
Just replace '00:00:00' and 3 hour to anything you want.
It just try to concat you time to a datetime string, and transform it into datetime then apply the interval operation. After that it apply the timediff to compute elapsed time by given unit(do not forget to % to get the correct domain). And finally concatenation the three differences.
This answer is kinda ugly, hope it is helpful to you.
It Should be
SELECT
TIME(SUBTIME(CONCAT(curdate(),' ','00:00:00'), 03:00:00)),
TIME(ADDTIME(CONCAT(curdate(),' ','00:00:00'), 03:00:00));

Selecting records overlapping with a given time range

Suppose I have the following table:
id | value | start_time | stop_time
-----------------------------------------------------------------
1 | value1 | 06:00:00 | 11:00:00
2 | value2 | 12:00:00 | 13:00:00
I need to select records whose time range overlap with 05:00–11:30. That means the record to be returned is row 1.
I tried using a query like this but it fails when the given time range crosses midnight. Perhaps it's impossible to do it without involving the date.
SELECT * FROM table WHERE
(
(start_time >= '05:00:00' AND stop_time <= '11:30:00') ||
(start_time >= '05:00:00' AND start_time <= '05:00:00') ||
(start_time <= '05:00:00' AND stop_time >= '11:30:00') ||
(stop_time >= '11:30:00' AND stop_time <= '05:00:00')
)
I'm thinking of enumerating all the minutes for the given time range and filtering using BETWEEN but there may be a better solution.
Dave,
The easiest way which i can see for you to achieve the desired result will be to use 'TIME' type for start_time and 'stop_time` fields.
Then you can select like this:
SELECT * FROM `YOUR_TABLE_NAME_HERE` WHERE `start_time` > '05:00:00' AND `stop_time` < '11:30:00'
Every range will need to be represented with a conditional, like so:
IF(start_time < stop_time, start_time >= '05:00:00' AND stop_time <= '11:30:00', start_time <= '05:00:00' AND stop_time >= '11:30:00')