How do I remove the date from a datetime stamp in Oracle? - sql

Very quick question, if I have a field, for example closed_date, which has a datetime, i.e.
01-JAN-19 09.00.00.000000000
And I would like to only look at cases closed between 09:00am and 10:00am each day, whats the syntax? How do I trunc the field just to time and only pull through cases which were closed each day between the 9am and 10am?
Many thanks

You can look at the hour portion alone; as you have a timestamp you can extract the hour number:
where extract(hour from closed_date) = 9
That will find rows there time is on or after 09:00:00, and before (not including) 10:00:00.
Usually when looking at data in a time period like 'between 9 and 10' you don't actually want to include the upper limit, because if you were also looking at data 'between 10 and 11' then data at exactly 10:00:00 would be included in both, which probably isn't what was intended. So it's common for date/time comparisons to use >= and < instead of between; you could also do this with a string comparison which you might consider clearer:
where to_char(closed_date, 'HH24:MI:SS') >= '09:00:00'
and to_char(closed_date, 'HH24:MI:SS') < '10:00:00'
or slightly more simply
where to_char(closed_date, 'HH24') >= '09'
and to_char(closed_date, 'HH24') < '10'
which in this case, as it's a single hour, is the same as:
where to_char(closed_date, 'HH24') = '09'
but then as you are only looking at the hour part anyway, extracting that as a number simplifies it even more (IMO).

You seem to be looking for a condition to filter a timestamp column based on its hour part. There are various ways to extract date parts from a timestamp, here is a solution that uses TO_CHAR :
TO_CHAR(closed_date, 'hh24') = '09'
This will match on timestamps whose time is higher than or equal to 9 AM and strictly smaller than 10 AM.

You can use the extract function from oracle
SELECT EXTRACT(HOUR FROM timestamp_col_value) AS CURRENT_HOUR
FROM DUAL;
Note that the extract function only works in certain combinations of MONTH/DAY/HOUR and date types. See here for more details.
If you want to extract a hour from a datetime, you need to convert it to a timestamp first, i.e.
SELECT EXTRACT(HOUR FROM CAST(SYSDATE AS TIMESTAMP)) AS CURRENT_HOUR
FROM DUAL;

select regexp_Replace('01-JAN-19 09.00.00.000000000','\d+-\w+-\d+ ') from dual
But if You want to search between hours, try EXTRACT, f.e.:
SELECT *
FROM your_table
WHERE to_char(your_column,
'yyyy-mm-dd hh24:mi:ss.ff a.m.',
'nls_date_language=american') in (9,10)
Updated answer. without nls_Date_language parameter, hour 9 o' clock could be also 9.pm and that is 21.

Related

select difference of two dates in hours and minutes

i have the following problem,
i am trying to find the difference of two dates in hours and minutes, for example
select to_date('13.05.2021 09:30','DD.MM.YYYY HH24:MI')
- to_date('13.05.2021 08:15','DD.MM.YYYY HH24:MI') from dual;
obiously it returns the difference in days, so the output will be 0,
i am expecting somthing like 01:15
Depending on the data type you need for your result...
If an interval day to second will work, then you can do this:
select (date2 - date1) * interval '1' day from dual;
For example:
select (to_date('13.05.2021 09:30','DD.MM.YYYY HH24:MI')
- to_date('13.05.2021 08:15','DD.MM.YYYY HH24:MI'))
* interval '1' day as diff
from dual;
DIFF
-------------------
+00 01:15:00.000000
Give this a try; if you need a different data type for the result, let us know. Note that, stupidly, Oracle doesn't support aggregate functions for intervals; so if you need a sum of such differences, you should apply the aggregation first, and only use this trick to convert to an interval as the last step.
In addition to #mathguy answer in case you need a specific output format:
TO_CHAR does not work with INTERVAL values. Either use EXTRACT
EXTRACT(HOUR FROM ((date2 - date1) DAY TO SECOND)) ||':'|| EXTRACT(MINUTE FROM ((date2 - date1) DAY TO SECOND))
or REGEXP_SUBSTR
REGEXP_SUBSTR((date2 - date1) DAY TO SECOND, ' \d{2}:\d{2}')
You may need some fine-tuning for proper output.

How does date manipulation/comparison/grouping work in SQL queries?

I need to analyze an SQL query (and construct its equivalent in MDX). I'm not familiar with SQL and can't access the database, so there are 5 simple things I can't figure out:
What does the part WHERE idate BETWEEN trunc(SYSDATE, 'iw')-7 AND trunc(SYSDATE, 'iw')-3 mean? Specifically:
What does subtracting 7 from trunc(SYSDATE, 'iw') do? Subtract 7 weeks or 7 days? I understand the trunc(...) expression is a value 0-53 corresponding to the week of the year, but it seems to clash with the label "previous week" and stated purpose of the query.
How does SQL compare dates? Are the values from trunc(...) evaluated as dates during comparison?
The query seems to group rows together if they happened in the same minute. However, the few rows of output I can see have 10-minute granularity (00:00, 00:10, 00:20, etc.) Is there something in the query that groups rows into 10 minute intervals, or is this a result of the input data?
Why are calls to substr() and to_char() and needed in the group by condition? What would happen if trunc(idate, 'HH24:MI') was used instead?
What does the pm do? There is also a cm that seems to have a similar function. Are these part of the temporary table names?
Finally, how do the hash marks (#) affect this query? I read it might be to signify temporary tables. If so, are these temporary tables created manually, or does something in the query cause them to be created?
For reference here is the query. (On a Oracle database, if it makes any difference.) Its purpose is to "analyze how firewall accept events are trending compared to last week":
SELECT 'Previous Week Average' AS term ,
Substr(To_char(idate, 'HH24:MI'), 0, 4)
|| '0' AS event_time ,
Round(Avg(tot_accept)) AS cnt
FROM (
SELECT *
FROM st_event_100_#yyyymm-1m#
WHERE idate BETWEEN trunc(SYSDATE, 'iw')-7 AND trunc(SYSDATE, 'iw')-3 #stat_monitor_group_query#
UNION ALL
SELECT *
FROM st_event_100_#yyyymm#
WHERE idate BETWEEN trunc(SYSDATE, 'iw')-7 AND trunc(SYSDATE, 'iw')-3 #stat_monitor_group_query# ) pm
GROUP BY substr(to_char(idate, 'HH24:MI'), 0, 4)
|| '0'
UNION ALL
SELECT 'Today' AS term ,
substr(to_char(idate, 'HH24:MI'), 0, 4)
|| '0' AS event_time ,
round(avg(tot_accept)) AS cnt
FROM st_event_100_#yyyymm# cm
WHERE idate >= trunc(SYSDATE) #stat_monitor_group_query#
GROUP BY substr(to_char(idate, 'HH24:MI'), 0, 4)
|| '0'
ORDER BY term DESC,
event_time ASC
iw truncates the date to the first day of the calendar week as defined by the ISO 8601 standard, which is Monday. When you subtract numbers from the date, it is always the number of days. So, idate BETWEEN trunc(SYSDATE, 'iw')-7 AND trunc(SYSDATE, 'iw')-3 gives you those dates that fall between previous week's Monday and Friday.
to_char(idate, 'HH24:MI') gives you the time(hour and minute) part in 24hr format. Ex: 14:33. By using substrin to extract only 4 characters, you are actually getting 14:3. So yes, this groups with a granularity of 10 mins.
You cannot write trunc(idate, 'HH24:MI'). It can only have 1 precision specifier.
If you write trunc(idate,'HH24'), it truncates to the hour. If you use MI, it truncates to the minute. So, to truncate it to 10 mins is a little tricky.
pm is just an alias for the whole subquery.
SELECT *
FROM st_event_100_#yyyymm-1m#
......
WHERE idate BETWEEN trunc(SYSDATE, 'iw')-7 AND trunc(SYSDATE, 'iw')-3 #stat_monitor_group_query#
# is part of the table anme in your query. It has no significance as such. But, it might be project/company specific.

Last date with time of the month

Need your help to conclude the query to fetch last date time of the sysdate month.
select to_char(last_day(sysdate),'DD-Mon-YYYY HH24:MI:SS') from dual
it gives last date as expected, but I need time as 23:59:00 which is not possible thru above query.
You could use TRUNC on next day i.e. SYSDATE + 1, and then subtract 60 seconds i.e. 60/86400 to get the desired output.
SQL> SELECT to_char((trunc(last_day(sysdate)) +1) - 60/86400,'DD-Mon-YYYY HH24:MI:SS') dt
2 FROM dual;
DT
--------------------
29-Feb-2016 23:59:00
SQL>
You could also use interval '1' minute or interval '60' second instead of 60/86400.
If you just want it for display for some reason you can hard-code the time into the format mask:
select to_char(last_day(sysdate), 'DD-Mon-YYYY "23:59:00"') from dual;
But you probably really want it as a date object, in which case you can add 23 hours and 59 minutes to the truncated (midnight) date, wchi is 1439 of the 1440 minutes in a day:
select to_char(trunc(last_day(sysdate)) + 1439/1440, 'DD-Mon-YYYY HH24:MI:SS')
from dual;
Or you can go to the next day and remove a minute, either with fractional days or with intervals:
select to_char(trunc(last_day(sysdate)) + interval '1' day - interval '1' minute,
'DD-Mon-YYYY HH24:MI:SS') from dual;
Generally if you're working with time periods you want to include up to 23:59:59, which you can also do with any of those methods, but as Damien_The_Unbeliever said in a comment, it's easier to compare against the start of the next period (e.g. < add_months(trunc(sysdate, 'MM'), 1). It's easy to accidentally miss part of a day by not taking the time into account properly, particularly if you actually have a timestamp rather than a date.

How to subtract hours from a date in Oracle so it affects the day also

I'm trying to subtract date from Oracle so it even effect the day as well. For example, if
the timestamp is 01/June/2015 00 hours and if I subtract 2 hours, I want to be able to go to to 31/May/2014 22 hours.
I tried
to_char(sysdate-(2/11), 'MM-DD-YYYY HH24')
but it only subtracts the hour; it does not touch the day itself.
Others have commented on the (incorrect) use of 2/11 to specify the desired interval.
I personally however prefer writing things like that using ANSI interval literals which makes reading the query much easier:
sysdate - interval '2' hour
It also has the advantage of being portable, many DBMS support this. Plus I don't have to fire up a calculator to find out how many hours the expression means - I'm pretty bad with mental arithmetics ;)
Try this:
SELECT to_char(sysdate - (2 / 24), 'MM-DD-YYYY HH24') FROM DUAL
To test it using a new date instance:
SELECT to_char(TO_DATE('11/06/2015 00:00','dd/mm/yyyy HH24:MI') - (2 / 24), 'MM-DD-YYYY HH24:MI') FROM DUAL
Output is: 06-10-2015 22:00, which is the previous day.
sysdate-(2/11)
A day consists of 24 hours. So, to subtract 2 hours from a day you need to divide it by 24:
DATE_value - 2/24
Using interval for the same:
DATE_value - interval '2' hour
date - n will subtract n days form given date. In order to subtract hrs you need to convert it into day buy dividing it with 24. In your case it should be to_char(sysdate - (2 + 2/24), 'MM-DD-YYYY HH24'). This will subract 2 days and 2 hrs from sysdate.
you should divide hours by 24 not 11
like this:
select to_char(sysdate - 2/24, 'dd-mon-yyyy HH24') from dual

Timestamp data type query in Oracle

I have a table called CYCLING_ACCIDENTS_2 containing a TIMESTAMP(6) column called ACC_DATE_TIME , this is an example of how the date is stored 31-MAY-12 16.45.00.000000, I would like to know how I can query just the time in such a date format so that I can have a time interval for all years I have (2005-2012) but just restricted to certain times in the day. I tried many functions but all I've got so far are syntax errors, I tried to search on the web but I can' t see anything appropriate to my case. Could anyone help?
Thanks!
First of all, a timestamp is a number, not a string. So the date is displayed by default as 31-MAY-12 16.45.00.000000, but it is actually the amount of microseconds since 1970 I believe.
If you want to select just the time part use to_char()
select to_char(acc_date_time, 'hh24:mi') time
, count(*) occurences
from cycling_accidents_2
group by to_char(acc_date_time, 'hh24:mi')
edit: I think this second query actually answers your question:
select *
from cycling_accidents_2 ca
where to_char(ca.acc_date_time, 'hh24:mi') between '10:00' and '18:00'
and ca.acc_date_time >= to_timestamp('01-01-2005', 'dd-mm-yyyy')
and ca.acc_date_time < to_timestamp('01-01-2013', 'dd-mm-yyyy')
SELECT * FROM CYCLING_ACCIDENTS_2 WHERE
(EXTRACT(YEAR FROM ACC_DATE_TIME) BETWEEN 2005 AND 2012)
AND
(EXTRACT(HOUR FROM ACC_DATE_TIME) BETWEEN 10 AND 18)