Displaying values between times - sql

I am trying to display the times that are between 03:00PM and 12:00AM within the same day. What can I do to get the desired results? I don't think my code should display anything with AM, but it does.
select TO_CHAR(orig_time, 'HH:MI AM') from orig_table
WHERE TO_CHAR(orig_time, 'HH:MI AM') > '03:00 PM'
AND TO_CHAR(orig_time, 'HH:MI AM') < '12:00 AM'
ORDER BY TO_CHAR(orig_time, 'AM'), TO_CHAR(orig_time, 'HH:MI');
TO_CHAR(
--------
05:00 AM
05:15 AM
06:46 AM
07:00 AM
08:00 AM
08:30 AM
08:33 AM
09:00 AM
09:05 AM
10:00 AM
10:10 AM
TO_CHAR(
--------
11:25 AM
11:30 AM
11:45 AM
05:00 PM
05:45 PM
05:58 PM
08:30 PM
09:10 PM
10:25 PM
11:20 PM

Your problem is that you are converting the values to a character type, which means comparisons are done alphabetically. That way, '05:00 AM' is greater than '03:00 PM'.
Several ways to fix this. One is converting to character, but using 24h based time:
select TO_CHAR(orig_time, 'HH:MI AM') from orig_table
WHERE TO_CHAR(orig_time, 'HH24:MI') > '15:00'
ORDER BY TO_CHAR(orig_time, 'HH24:MI');
Notice that you do not need to check for before midnight doing this way.
Additionally, you want to extend it to, say, 1AM in the next day, you'd have to start using whole dates portion. Also, use the BETWEEN keyword in that case.

If you mean midnight
select numtodsinterval(orig_time-trunc(orig_time)) as time
from orig_table
WHERE numtodsinterval(orig_time-trunc(orig_time)) > interval '15' hour
/
where there's no need to check for hour < 24
If you mean midday
select numtodsinterval(orig_time-trunc(orig_time),'day') as time
from orig_table
WHERE numtodsinterval(orig_time-trunc(orig_time),'day') between interval '12' hour and interval '15' hour
/

select TO_CHAR(orig_time, 'HH:MI AM') from orig_table
WHERE TO_CHAR(orig_time, 'HH24:MI') > '15:00'
AND TO_CHAR(orig_time, 'HH24:MI:SS') <= '23:59:59'
ORDER BY TO_CHAR(orig_time, 'AM'), TO_CHAR(orig_time, 'HH:MI');

Related

Custom range field based on Hour and time

I want to create a Custom field like below based on below two fields (Hour and Time). Does anyone know how to do this is SQL?
You do not need the Hour column for this result. The Time values are enough.
Sample data
create table data
(
TimeValue time(0)
);
insert into data (TimeValue) values
('12:00:00 AM'),
('12:15:00 AM'),
('12:30:00 AM'),
('12:45:00 AM'),
( '1:00:00 AM'),
( '1:15:00 AM'),
( '1:30:00 AM'),
( '1:45:00 AM'),
( '2:00:00 AM'),
( '2:15:00 AM'),
( '2:30:00 AM'),
( '2:45:00 AM'),
( '3:00:00 AM'),
( '3:15:00 AM'),
( '3:30:00 AM'),
( '3:45:00 AM'),
( '4:00:00 AM'),
( '4:15:00 AM'),
( '4:30:00 AM');
Solution
select 'Hour ' + convert(nvarchar(2), datepart(hour, d.TimeValue)) as [Hour],
convert(nvarchar(11), d.TimeValue, 22) as [Time],
'Hour ' +
convert(nvarchar(2), case
when datepart(hour, d.TimeValue)-1 < 0 then 0
else datepart(hour, d.TimeValue)-1
end) + '-' +
convert(nvarchar(2), datepart(hour, d.TimeValue)) as [Custom]
from data d;
Result
Hour Time Custom
------ ----------- --------
Hour 0 12:00:00 AM Hour 0-0
Hour 0 12:15:00 AM Hour 0-0
Hour 0 12:30:00 AM Hour 0-0
Hour 0 12:45:00 AM Hour 0-0
Hour 1 1:00:00 AM Hour 0-1
Hour 1 1:15:00 AM Hour 0-1
Hour 1 1:30:00 AM Hour 0-1
Hour 1 1:45:00 AM Hour 0-1
Hour 2 2:00:00 AM Hour 1-2
Hour 2 2:15:00 AM Hour 1-2
Hour 2 2:30:00 AM Hour 1-2
Hour 2 2:45:00 AM Hour 1-2
Hour 3 3:00:00 AM Hour 2-3
Hour 3 3:15:00 AM Hour 2-3
Hour 3 3:30:00 AM Hour 2-3
Hour 3 3:45:00 AM Hour 2-3
Hour 4 4:00:00 AM Hour 3-4
Hour 4 4:15:00 AM Hour 3-4
Hour 4 4:30:00 AM Hour 3-4
Fiddle to see things in action.
I want to create a Custom field like below based on below two fields (Hour and Time).
Assuming that you want the column generated from the hour column and the "hour" from the time column, then you can use a generated column:
alter table t add custom as
(concat(hour, '-', datepart(hour, time));
This is now part of the table. If you just want the value in a result set, you can put the expression in a result set.
Note: This doesn't return the results you have specified. You haven't explained the logic for those results.

Oracle Sql adding hours and minute to a to_date while insert statment

Hello I am trying to make an insert statement which includes adding 2 hours and 10 minutes to a to_date .
But i do not know which function i can use.
this is my statement so far
insert into xyz values (TO_DATE('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM'))
but i want to add 2 hours and 10 minutes to the above while inserting.
Use date arithmetics. In Oracle, you can add decimal values to a date (1 stands for "1 day"):
insert into xyz
values (
to_date('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM')
+ 2/24 + 10 / 60 / 24
)
Or:
insert into xyz
values (
to_date('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM')
+ interval '2' hour + interval '10' minute
)
Or (with credits to Wernfried Domscheit):
insert into xyz
values (
to_date('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM')
+ interval '2:10' hour to minute
)
You can use interval as well,
insert into xyz values (TO_DATE('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM') + interval '130' minute);
select TO_DATE('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM') + interval '130' minute from dual;
TO_DATE('22-OCT-202
-------------------
22-10-2020 01:10 PM
select TO_DATE('22-Oct-2020 11:00 AM', 'DD-MM-YYYY HH:MI AM') + interval '1' day from dual;
TO_DATE('22-OCT-202
-------------------
23-10-2020 11:00 AM

SQL MONTH AVERGE

Here is what I got to get the duration of the two.
select (TO_TIMESTAMP(s.END, 'hh24:mi:ss') - TO_TIMESTAMP(s.START, 'hh24:mi:ss')) from SESSION s;
I am trying to get the average per month. I do have a time dimension that I can refer to using month_name from the table. However, how to I do this? I have tried to use add_months by casting it but got no luck.
Thank you guys.
VARCHAR for all.
DATE START END RESULT OF QUERY AVERAGE PER MONTH
13-FEB-11 16:00:00 18:00:00 +00 02:00:00.000000 ?
02-APR-11 08:30:00 10:30:00 +00 02:00:00.000000
02-APR-11 16:00:00 18:00:00 +00 02:00:00.000000
05-APR-11 08:30:00 10:30:00 +00 02:00:00.000000
05-APR-11 16:00:00 18:00:00 +00 02:00:00.000000
I would suggest turning the scripts to dates instead of timestamps. This gives you a numeric result that represents the interval as days, which, unlike an interval, you can average.
If you want the average session duration per month, you can aggregate like so:
select
trunc(to_date(s.date, 'dd-mon-yy'), 'mm') mnt,
avg(to_date(s.end, 'hh24:mi:ss') - to_date(t.start, 'hh24:mi:ss')) avg_duration
from session s
group by trunc(to_date(s.date, 'dd-mon-yy'), 'mm')
If you want the result as an interval, you can use numtodsinterval() on the results of the computation:
select
trunc(to_date(s.date, 'dd-mon-yy'), 'mm') mnt,
numtodsinterval(
avg(to_date(s.end, 'hh24:mi:ss') - to_date(t.start, 'hh24:mi:ss')),
'day'
) avg_interval
from session s
group by trunc(to_date(s.date, 'dd-mon-yy'), 'mm')

How to extract time from a date column in the where clause in ORACLE?

I want to select rows with a specific time interval but the date doesn't matter. So I need a function to return just the time part. I tried using:
to_char(mydate, 'HH12:MI:SS') between '00:00:00' and '08:00:00'
but this doesn't seem to work. Any ideas?
With some sample data you can see that using HH12 doens't necessarly produce the strings you are expecting:
alter session set nls_date_format = 'YYYY-MM-DD HH24:MI:SS';
-- CTE just for dummy data
with mytable (mydate) as (
select cast(timestamp '2018-08-01 00:00:00' as date) from dual
union all select cast(timestamp '2018-08-02 07:59:59' as date) from dual
union all select cast(timestamp '2018-08-03 08:00:00' as date) from dual
union all select cast(timestamp '2018-08-04 08:00:01' as date) from dual
union all select cast(timestamp '2018-08-05 19:59:59' as date) from dual
union all select cast(timestamp '2018-08-06 20:00:00' as date) from dual
union all select cast(timestamp '2018-08-07 20:00:01' as date) from dual
)
-- actual query
select mydate,
to_char(mydate, 'HH24:MI:SS') as time_24,
to_char(mydate, 'HH12:MI:SS') as time_12
from mytable;
MYDATE TIME_24 TIME_12
------------------- -------- --------
2018-08-01 00:00:00 00:00:00 12:00:00
2018-08-02 07:59:59 07:59:59 07:59:59
2018-08-03 08:00:00 08:00:00 08:00:00
2018-08-04 08:00:01 08:00:01 08:00:01
2018-08-05 19:59:59 19:59:59 07:59:59
2018-08-06 20:00:00 20:00:00 08:00:00
2018-08-07 20:00:01 20:00:01 08:00:01
So when you try to filter using that HH12-based string it includes records you don't expect to see, between 8am and 8pm; and also excludes midnight (as that is '12:00:00' not '00:00:00'):
select mydate
from mytable
where to_char(mydate, 'HH12:MI:SS') between '00:00:00' and '08:00:00';
MYDATE
-------------------
2018-08-02 07:59:59
2018-08-03 08:00:00
2018-08-05 19:59:59
2018-08-06 20:00:00
If you use HH24 instead then you get
select mydate
from mytable
where to_char(mydate, 'HH24:MI:SS') between '00:00:00' and '08:00:00';
MYDATE
-------------------
2018-08-01 00:00:00
2018-08-02 07:59:59
2018-08-03 08:00:00
Also, notice that between is inclusive, so it picks up records at exactly 08:00:00. That may not be what you want - if you're splitting the day into three 8-hour periods, you don't data for that second to be included multiple times; so you can use a more explicit range instead:
select mydate
from mytable
where to_char(mydate, 'HH24:MI:SS') >= '00:00:00'
and to_char(mydate, 'HH24:MI:SS') < '08:00:00';
MYDATE
-------------------
2018-08-01 00:00:00
2018-08-02 07:59:59
then your second shift is:
where to_char(mydate, 'HH24:MI:SS') >= '08:00:00'
and to_char(mydate, 'HH24:MI:SS') < '16:00:00';
and your third shift is:
where to_char(mydate, 'HH24:MI:SS') >= '16:00:00';
or if you prefer, for consistency:
where to_char(mydate, 'HH24:MI:SS') >= '16:00:00'
and to_char(mydate, 'HH24:MI:SS') < '24:00:00';
You can't ever get the hour reported as 24 but as it's a string comparison that doesn't matter here, though it is slightly jarring.
TO_CHAR(mydate, 'HH24:MI:SS')
WHERE mydate BETWEEN '00:00:00' AND '08:00:00';

Oracle SQL: Handle with Daylight Saving Time

I have the following system information:
I use Oracle Database 10g
The SysTimeStamp is UTC
The SessionTimeZone is Europe/Athens
The dbTimeZone is +03:00
So, I have the column date_1 from tbl_1 table, with the following datetime:
date_1
-----------------
08.02.2017 10:00
08.02.2017 11:00
08.02.2017 12:00
-----------------
The results I want is like this:
date_2
-----------------
08.02.2017 13:00
08.02.2017 14:00
08.02.2017 15:00
For that I use:
SELECT TO_CHAR(date_1 + INTERVAL '3' HOUR, 'DD.MM.YYYY HH24:MI') as date_2
FROM tbl_1
WHERE date_1 >= TO_DATE('08.02.2017 10:00','DD.MM.YYYY HH24:MI')
AND date_1 <= TO_DATE('08.02.2017 12:00','DD.MM.YYYY HH24:MI')
My problem appear when the hour from March and October is changing because in the last Sunday from March we have 23 hours in a day and in the last Sunday from October we have 25 hours in a day.
Because of this I have to change my query 4 times/year (On summer time, on winter time, when we have 23 hour in March and when we have 25 hour in October)
Can you recommend a query in this select that solve this problem?
If you have a plain date or timestamp with no embedded time zone information, you can tell Oracle to treat it as being in a specific time zone with the from_tz() function. You can then convert that value - which now has data type 'timestamp with zone zone' rather than a plain 'timestamp' - to another zone with the at time zone datetime expression syntax, either using the session time zone as 'local' or with a specific named time zone:
alter session set nls_date_format='YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_format='YYYY-MM-DD HH24:MI:SS';
alter session set nls_timestamp_tz_format='YYYY-MM-DD HH24:MI:SS TZR';
alter session set time_zone = 'America/New_York';
with cte (ts) as (
select timestamp '2017-02-08 12:00:00' from dual
)
select ts,
from_tz(ts, 'UTC') as ts_utc,
from_tz(ts, 'UTC') at local as ts_local,
from_tz(ts, 'UTC') at time zone 'Europe/Athens' as ts_athens
from cte;
TS TS_UTC TS_LOCAL TS_ATHENS
------------------- ----------------------- ------------------------------------ ---------------------------------
2017-02-08 12:00:00 2017-02-08 12:00:00 UTC 2017-02-08 07:00:00 AMERICA/NEW_YORK 2017-02-08 14:00:00 EUROPE/ATHENS
If you're starting from a date then you have to convert it to a timestamp before calling from_tz():
with cte (dt) as (
select cast( timestamp '2017-02-08 12:00:00' as date) from dual
)
select dt,
from_tz(cast(dt as timestamp), 'UTC') as ts_utc,
from_tz(cast(dt as timestamp), 'UTC') at local as ts_local,
from_tz(cast(dt as timestamp), 'UTC') at time zone 'Europe/Athens' as ts_athens
from cte;
DT TS_UTC TS_LOCAL TS_ATHENS
------------------- ----------------------- ------------------------------------ ---------------------------------
2017-02-08 12:00:00 2017-02-08 12:00:00 UTC 2017-02-08 07:00:00 AMERICA/NEW_YORK 2017-02-08 14:00:00 EUROPE/ATHENS
So the data type of your original date_1 values matters, as does the nominal time zone it is supposed to represent. If it's a;ready a 'timestamp with time zone' or 'timestamp with local time zone' then it already has embedded time zone information, so you don't need the from_tz() part at all. If it's a date you need to convert it to a timestamp.
Assuming that date_1 is stored as a plain timestamp (maybe implied by your interval addition, but not by the column name and filters you used) and that it's nominally UTC, you could do:
from_tz(date_1, 'UTC') at time zone 'Europe/Athens'
... which will give you a 'timestamp with time zone' result; or you could use local to rely on your session time zone. If `date_1 is stored as a date you'd add the conversion to timestamp:
from_tz(cast(date_1 as timestamp), 'UTC') at time zone 'Europe/Athens'
As a demo, generating timestamps (not dates) in a CTE including some around the DST change for this year:
with tbl_1(date_1) as (
select timestamp '2017-02-08 10:00:00' from dual
union all select timestamp '2017-02-08 11:00:00' from dual
union all select timestamp '2017-02-08 12:00:00' from dual
union all select timestamp '2017-03-23 12:00:00' + numtodsinterval(level, 'day')
from dual connect by level <= 4
)
select date_1,
-- cast(from_tz(date_1, 'UTC') at time zone 'Europe/Athens' as timestamp) as date_2
to_char(from_tz(date_1, 'UTC') at time zone 'Europe/Athens',
'DD.MM.YYYY HH24:MI') as date_2
from tbl_1
order by date_1;
DATE_1 DATE_2
------------------- ----------------
2017-02-08 10:00:00 08.02.2017 12:00
2017-02-08 11:00:00 08.02.2017 13:00
2017-02-08 12:00:00 08.02.2017 14:00
2017-03-24 12:00:00 24.03.2017 14:00
2017-03-25 12:00:00 25.03.2017 14:00
2017-03-26 12:00:00 26.03.2017 15:00
2017-03-27 12:00:00 27.03.2017 15:00
You can see that an extra hour is added automatically after the clocks change on March 26th. But the results are out by an hour for your sample February data - so either your data isn't actually stored as UTC (but is -01:00, and you can change the from_tz() call to reflect that), or your expected results are wrong.
You can apply a case to the select:
select date_1 + case
when to_char(date_1 ,'MM') <= 3 then 2/24 -- Jan/Feb/Mar
when to_char(date_1,'MM') <= 10 then 3/24 -- Apr to Oct
else 2/24 -- Nov/Dec
end as date_2
from tbl_1
For USA timezone
SELECT SYSDATE,
NEXT_DAY ( TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') + 7 dst_start,
NEXT_DAY ( TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/11/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') dst_end,
CASE WHEN SYSDATE >= NEXT_DAY ( TO_DATE ( TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') + 7 AND SYSDATE < NEXT_DAY ( TO_DATE ( TO_CHAR (SYSDATE, 'YYYY') || '/11/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') THEN 'Y' ELSE 'N' END AS dst_check_usa,
NEW_TIME ( SYSDATE, CASE WHEN SYSDATE >= NEXT_DAY ( TO_DATE ( TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') + 7 AND SYSDATE < NEXT_DAY ( TO_DATE ( TO_CHAR (SYSDATE, 'YYYY') || '/11/01 02:00 AM', 'YYYY/MM/DD HH:MI AM') - 1, 'SUN') THEN 'CDT' ELSE 'CST' END, 'GMT') AS current_time_gmt
FROM DUAL;
For Europe Timezone
SELECT SYSDATE,
NEXT_DAY(LAST_DAY(TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM'))-7, 'SUN') dst_start_uk,
NEXT_DAY(LAST_DAY(TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/10/01 02:00 AM', 'YYYY/MM/DD HH:MI AM'))-7, 'SUN') dst_end_uk,
CASE WHEN SYSDATE >= NEXT_DAY(LAST_DAY(TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/03/01 02:00 AM', 'YYYY/MM/DD HH:MI AM'))-7, 'SUN') AND SYSDATE < NEXT_DAY(LAST_DAY(TO_DATE (TO_CHAR (SYSDATE, 'YYYY') || '/10/01 02:00 AM', 'YYYY/MM/DD HH:MI AM'))-7, 'SUN') THEN 'Y' ELSE 'N' END AS dst_check_uk
FROM DUAL;