Case statement based on the time - sql

I have the following table
Based on the transaction_DT:
if the transaction between 04:00:00 PM to 08:00:00 AM + next day get "After Hour"
Between 08:00:00 AM to 04:00:00 PM within same day get "Working Hour"
Using the case statement it is not works!
CASE
WHEN ( Transacton_DT >= TO_DATE ('4:00:00 PM', 'HH:MI:SS PM')
AND Transacton_DT <= TO_DATE ('11:59:00 PM', 'HH:MI:SS PM') )
OR ( Transacton_DT >= TO_DATE ('12:01:00 AM', 'HH:MI:SS AM')
AND Transacton_DT <= TO_DATE('8:00:00 AM', 'HH:MI:SS AM') )
THEN
'After Hour'
ELSE
'Working Hour'
END AS "Shift"

Hmmm . . . how about something like this:
(case when to_char(transaction_dt, 'HH24:MI') between '08:00' and '16:00'
then 'Working hours' else 'After hours'
end)
Your code doesn't work because you are comparing a value with a time component only (well a default date component) to one with a date component.

case
when to_char(transaction_dt, 'HH24:MI') between '08:00' and '23:59'
or to_char(transaction_dt, 'HH24:MI') between '00:00' and '07:59'
then 'Working hours'
else 'After hours' end

Related

Subtracting time in oracle sql

I am trying to subtract time from each other based on a case condition
but the error is am getting is non-numeric character found where numeric character was expected
HERE IS THE CODE
select
case
when to_date('01-JAN-2019 05:00 AM', 'HH:MI:SS AM') <
to_date('01-JAN-2019 05:00 PM', 'HH:MI:SS AM')
then round((to_date('01-JAN-2019 05:00 AM', 'HH:MI:SS AM') -
to_date('01-JAN-2019 09:00 AM', 'HH:MI:SS AM'))*24,2)
else 0
end late
from dual
One option would be using ISO 8601 standard timestamp format as
select case
when timestamp'2019-01-01 05:00:00' < timestamp'2019-01-01 17:00:00'
then
timestamp'2019-01-01 05:00:00' - timestamp'2019-01-01 09:00:00'
else interval '0' second
end as late
from dual
If you need numeric value for hour only, then consider :
select case
when timestamp'2019-01-01 05:00:00' < timestamp'2019-01-01 17:00:00'
then
extract( hour from timestamp'2019-01-01 05:00:00'
- timestamp'2019-01-01 09:00:00' )
else 0
end as late
from dual
Demo
You have to match date format and format mask:
SQL> select
2 round(
3 (to_date('01-JAN-2019 05:00 AM', 'dd-mon-yyyy HH:MI AM') -
4 to_date('01-JAN-2019 09:00 AM', 'dd-mon-yyyy HH:MI AM')
5 ) * 24, 2) result
6 from dual;
RESULT
----------
-4
SQL>
First conversion format and real string format must match. Next, convert date to timestamp before substracting, so the result is interval. Finally, extract parts needed from the interval
select
extract( day from diff ) Days,
extract( hour from diff ) Hours,
extract( minute from diff ) Minutes
from (
select CAST(to_date('01-JAN-2019 09:00 AM', 'DD-MON-YYYY HH:MI AM') as timestamp)
- CAST(to_date('01-JAN-2019 05:00 AM', 'DD-MON-YYYY HH:MI AM') as timestamp) diff
from dual
)

How to grab the value for the previous hour when the data type is TIMESTAMP with TIMEZONE

So I have some logic that will try to grab the Value (VALUE) correlated to the previous hour if the criteria are met. The HOUR column is a TIMESTAMP with TIME ZONE column. I figured I can run the following query but got an ORA-00932 inconsistent datatypes: expected TIMESTAMP WITH TIME ZONE got NUMBER error. Is there some sort of conversion function I have to add to my 'timestamp with timezone' value?
Below is my query code:
SELECT MAX(VALUE)
FROM VALUE V
WHERE CODE = 'HI'
AND HR = '15-JAN-17 05.00.00.000000000 AM' - (1/24);
Thanks in advance.
'15-JAN-17 05.00.00.000000000 AM' is a string, not a timestamp. You can convert it to a timestamp (with no time zone) as #D-Shih suggested, but you should specify the format mask and the date language rather than relying on NLS settings:
AND HR = to_timestamp('15-JAN-17 05.00.00.000000000 AM', 'DD-MON-RR HH.MI.SS.FF AM',
'NLS_DATE_LANGUAGE=ENGLISH') - (1/24);
or if it's a fixed value (presumably it isn't, or you could just change that literal):
AND HR = timestamp '2017-01-15 05:00:00' - (1/24);
Subtracting a number of days from a timestamp gives you a date result, so you perhaps really want to do:
AND HR = timestamp '2017-01-15 05:00:00' - interval '1' hour;
This now stays as a timestamp, but you have no time zone information. If you know the time zone you can include it in string literal and format mask, or in the timestamp literal, e.g.:
AND HR = timestamp '2017-01-15 05:00:00 America/Los_Angeles' - (1/24);
or from your original string, if that's all you have to work with, you can use from_tz():
AND HR = from_tz(to_timestamp('15-JAN-17 05.00.00.000000000 AM', 'DD-MON-RR HH.MI.SS.FF AM',
'NLS_DATE_LANGUAGE=ENGLISH'), 'America/Los_Angeles') - interval '1' hour;
Doing the interval subtraction last should mean that it handles DST properly.
Demo of the various conversions, starting from your string value:
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_format = 'YYYY-MM-DD HH24:MI:SS.FF1';
alter session set nls_timestamp_tz_format = 'YYYY-MM-DD HH24:MI:SS.FF1 TZR TZD';
select
to_timestamp('15-JAN-17 05.00.00.000000000 AM', 'DD-MON-RR HH.MI.SS.FF AM',
'NLS_DATE_LANGUAGE=ENGLISH') as a_timestamp,
to_timestamp('15-JAN-17 05.00.00.000000000 AM', 'DD-MON-RR HH.MI.SS.FF AM',
'NLS_DATE_LANGUAGE=ENGLISH') - (1/24) as b_date,
to_timestamp('15-JAN-17 05.00.00.000000000 AM', 'DD-MON-RR HH.MI.SS.FF AM',
'NLS_DATE_LANGUAGE=ENGLISH') - interval '1' hour as c_timestamp,
from_tz(to_timestamp('15-JAN-17 05.00.00.000000000 AM', 'DD-MON-RR HH.MI.SS.FF AM',
'NLS_DATE_LANGUAGE=ENGLISH'), 'America/Los_Angeles') - interval '1' hour as d_timestamp_tz
from dual;
A_TIMESTAMP B_DATE C_TIMESTAMP D_TIMESTAMP_TZ
--------------------- ------------------- --------------------- ---------------------------------------------
2017-01-15 05:00:00.0 2017-01-15 04:00:00 2017-01-15 04:00:00.0 2017-01-15 04:00:00.0 AMERICA/LOS_ANGELES PST
use TO_TIMESTAMP to let '15-JAN-17 05.00.00.000000000 AM' to datetime then minus one hour.
SELECT MAX(VALUE)
FROM VALUE V
WHERE CODE = 'HI'
AND HR = TO_TIMESTAMP('15-JAN-17 05.00.00.000000000 AM','DD-MON-RR HH.MI.SS.FF AM') - (1/24);

How to check if current time falls between two time stamps in oracle

How to check if my current time stamp is between a time range.
for ex: I want to check my current time(let's say 03:00 PM) falls between 06:00 PM and 07:00 PM.
How do I check this in Oracle?
SELECT CASE
WHEN SYSDATE - TRUNC( SYSDATE ) BETWEEN 17.5/24 AND 18.5/24
THEN 'Is between 17:30 and 18:30'
ELSE 'Not between 17:30 and 18:30'
END
FROM DUAL;
or
SELECT CASE
WHEN SYSDATE BETWEEN TRUNC( SYSDATE ) + INTERVAL '17:30' HOUR TO MINUTE
AND TRUNC( SYSDATE ) + INTERVAL '18:30' HOUR TO MINUTE
THEN 'Is between 17:30 and 18:30'
ELSE 'Not between 17:30 and 18:30'
END
FROM DUAL;
I'd use strings for the comparision:
select
case when to_char(sysdate, 'HH24:MI') >= '18:00'
and to_char(sysdate, 'HH24:MI') < '19:00' then 'YES' else 'NO'
end as in_time_range
from dual;

cast(date as timestamp) comparison is incorrect

When I'm executing this comparison, it is giving incorrect result.
select case when cast('06-JAN-17 12.48.14.243000000 PM' as timestamp) >
cast('06-JAN-17 08.08.57.000000000 PM' as timestamp) then 1 else 0 end from dual
the result is '0' instead of '1'. Could someone help me what is wrong in this?
You have to use TO_TIMESTAMP when you like to cast a string to TIMESTAMP properly.
when TO_TIMESTAMP('06-JAN-17 12.48.14.243000000 PM', 'DD-MON-RR HH.MI.SS.FF PM') >
TO_TIMESTAMP('06-JAN-17 08.08.57.000000000 PM', 'DD-MON-RR HH.MI.SS.FF PM')
then 1 else 0 end
You first need to convert your text literal to a date before you cast it to a timestamp. I show you an example:
select case when cast(to_date('06-01-17 12:48:14', 'DD-MM-YY HH:MI:SS') as timestamp) > cast(to_date('06-01-17 08:08:57', 'DD-MM-YY HH:MI:SS') as timestamp) then 1 else 0 end from dual
This resturns 1.
Just change my literal by yours and the passing date format and your result will work as well.

Generate a table from period of time and each entry spans one hour

For example I have 8am to 8pm.
I want the table where it can generate the table where it has entry one by one starttime and endtime (two columns):
8:00-9:00
9:00-10:00
....
19:00-20:00
Like this way. No date before the time.
You can use the generate_series function to do that:
psql# select generate_series('2014-01-01 16:00'::timestamp, '2014-01-01 20:00'::timestamp, '1 hour');
generate_series
---------------------
2014-01-01 16:00:00
2014-01-01 17:00:00
2014-01-01 18:00:00
2014-01-01 19:00:00
2014-01-01 20:00:00
(5 rows)
And then you can use:
SELECT
t AS starttime,
t + INTERVAL '1 hour' as endtime
FROM
GENERATE_SERIES(
'2014-01-01 16:00'::TIMESTAMP,
'2014-01-01 20:00'::TIMESTAMP,
'1 hour'
) AS t
To get the start and end times.
Alternatively, to just get the times, you can use:
SELECT
'08:00'::time + (t || ' hours')::interval as starttime,
'08:00'::time + ((t + 1)::text || ' hours')::interval as endtime
FROM
GENERATE_SERIES(0, 12) AS t
Simpler, use a full timestamp with a dummy date component and cast to time:
SELECT t::time AS starttime
, t::time + interval '1h' AS endtime
FROM generate_series('2000-1-1 08:00'::timestamp
, '2000-1-1 19:00'::timestamp
, interval '1h') t;
The same for text columns:
SELECT to_char(t, 'HH24:MI') AS starttime
, to_char(t + interval '1h', 'HH24:MI') AS endtime
FROM generate_series('2000-1-1 08:00'::timestamp
, '2000-1-1 19:00'::timestamp
, interval '1h') t;