Timestamp data type query in Oracle - sql

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)

Related

Query with CASE WHEN / LAST_DAY is not giving any output

Running below query and not getting the output. Can someone please tell whats wrong in it?
Select distinct (table.datex)
from table
where table.datex =
(
CASE when extract( day from sysdate) >=19
then last_day(add_months(sysdate, -1))
else last_day(add_months(sysdate, -2))
END
)
Sample data
Datex
ID
30-JUN-21
A
31-MAY-21
B
29-JUN-21
C
Expected result
Datex
30-JUN-21
When I am passing the value hard-coded(calculated by the case) to where clause it's working fine, but when I apply the case it's not working. No error. No output is coming.
Date or datetime?
Oracle's LAST_DAY doesn't do what the name suggests, and the docs (https://docs.oracle.com/en/database/oracle/oracle-database/21/sqlrf/LAST_DAY.html#GUID-296C7C02-7FB9-4AAC-8927-6A79320CE0C6) fail to explain that, too.
Unlike several other DBMS Oracle doesn't have a date type. It only has a datetime type and they even call that inappropriately DATE. This means that a "date" in Oracle always has a time part. A date with its time part set to 00:00:00 can be considered a day's midnight (i.e. the very beginning of the day) or the whole day.
The function SYSDATE gives us a date in the sense of the DATE datatype, not in the sense of a real day, i.e. it gives us the datetime of "now", e.g. 2021-07-20 14:38:00. ADD_MONTHS changes the month in that datetime (and sometimes the year and sometimes even the day), i.e. leaves the time part untouched. LAST_DAY, too, changes the date part to get to the last day of the month, but leaves the time part untouched.
Your CASE expression hence results in something like TIMESTAMP '2021-07-20 14:38:00' and not in DATE '2021-07-20' as one might expect.
You say that you tried your query with the date you computed wth your case expression, and it worked. Did you compute the resulting day in your head or with a query? If the latter: The tool you are using may be set to only display a datetime's date part and omit the time part. This would explain why you only saw 30-JUN-21 when checking the CASE expression.
Solution
Truncate the datetime down to a whole day
Select distinct datex
from mytable
where (extract(day from sysdate) >=19 and datex = trunc(last_day(add_months(sysdate, -1))))
or (extract(day from sysdate) < 19 and datex = trunc(last_day(add_months(sysdate, -2))))
It doesn't matter whether you apply TRUNC late as in my example or right away on SYSDATE (with TRUNC(SYSDATE)) by the way. The only aim is to get rid of the time part at some point in the expression.
Don't use case in where clauses. Boolean logic can handle that.
And take a look if it is really the condition you want
Select distinct datex
from your_table
where
(
extract(day from sysdate) >=19
and datex = last_day(add_months(sysdate,-1))
)
or
(
extract(day from sysdate) < 19
and datex = last_day(add_months(sysdate,-2))
)

Oracle SQL Current_Date

I’m experiencing some issues while using the Current_Date function in a simple query and I haven’t been able to figure out why. I’m working in an Oracle 12c environment using Oracle SQL Developer 3.2.
My original query looks something like this:
select * from Inventory where Placement_End_Dt >= Current_date
The above works fine except it doesn’t pick up records where Placement_End_Dt is today (14th May 18)
I attempted to simplify the query as follows, but this also returns nothing
select * from Inventory where Placement_End_Dt = Current_date
However when I apply date formatting as follows, it works:
select * from Inventory where to_char(Placement_End_Dt, 'DD-MM-YYYY') = to_char(Current_date, 'DD-MM-YYYY')
Then I try and expand on this to revert to my original query to select all records with an end date from today onwards:
select * from Inventory where to_char(Placement_End_Dt, 'DD-MM-YYYY') => to_char(Current_date, 'DD-MM-YYYY')
This fails spectacularly because it selects records with a Placement_End_Dt past, present and future!
The Placement_End_Dt columns is defined as an Oracle DATE data type
Would appreciate some input on how I can get this query to work.
When using to_char you are comparing strings.
to_char(date '2000-01-20', 'DD-MM-YYYY') > to_char(date '2018-05-14', 'DD-MM-YYYY')
because '20-01-2000' is greater than '14-05-2018', because of the first letters in the strings: '2' > '1'.
And CURRENT_DATE is hardly ever used, because it uses your computer's time, rather than the database time, so you can easily be some hours off. Use SYSDATE instead.
I would suggest using this query
select * from Inventory where trunc(Placement_End_Dt) = trunc(sysdate);
Oracle Date columns also store a timestamp by default, so unless the records were the same down to the second, they won't match. When you use trunc() on a date column, it truncates the timestamp and leaves just the date.
Try this, it will return the rows for present days and future days.
select * from Inventory where trunc(Placement_End_Dt) >= trunc(sysdate);

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.

What should be the outcome of TRUNC('02-MAY-14','HH24')?

I found a bizarre snippet which is confusing me so I thought I'll ask the experts.
Let assume a tableA got following columns with data:
"START_TIME":1399075198
"END_TIME":1399075200
"START_DATE":"02-MAY-14"
"END_DATE":"03-MAY-14"
Now query 1:
SELECT MIN(start_date) INTO sdate FROM tableA;
query 2:
SELECT TRUNC(sdate, 'HH24') + INTERVAL '30' MINUTE from dual;
So if start-date = '02-MAY-14', how would that truncate to 'HH24'?
The expression:
TRUNC(sdate, 'HH24')
cuts off everything from a date that is smaller than an hour, i.e. the minutes and seconds. For the specific date:
TRUNC('02-MAY-14','HH24')
it returns the date unchanged. It only makes sense if the Oracle date contains a time component.
Possibly, your SQL tool (SQL Developer, TOAD etc.) is configured to not display the time part of Oracle dates. So the original date might in fact be 02-MAY-14 09:03:25. Then it would return:
02-MAY-14 09:00:00
You mention the columns START_TIME and END_TIME but don't use them in the SQL queries. What are they for?
As start_date does not have a time part in your example, TRUNC is superfluous here. If however it had a timepart, if for example start_time had been added to start_date before, then TRUNC would remove minutes, seconds and microseconds, only keeping the date and hour because of 'HH24' which means "truncate datetime down to full hour".
In Oracle the date datatype inherently store the time as well.
Try executing the below query. It should clear things up a bit:
SELECT TO_CHAR(SYSDATE,'DD-MON-YYYY HH:MI:SS'), TO_CHAR(TRUNC(SYSDATE,'HH24'),'DD-MON-YYYY HH:MI:SS') FROM DUAL;

Oracle SQL - Determine if a timestamp falls within a range, excluding date

I'm looking for a way to determine if a timestamp falls between two times, regardless of the date in that timestamp. So for example, if the time in the timestamp falls between '00:00:00.000' (midnight) and '01:00:00.000' (1 A.M.), I'd want to select that row regardless of the particular date.
I've tried lots of different variations on the to_char and to_date functions, but I keep getting errors. Coming from Informix, Oracle seems much more complicated.
The thing closest to "correct" (I think) that I've tried is:
SELECT *
FROM my_table
WHERE SUBSTR(TO_CHAR(my_timestamp), 10) > '00:00:00.000'
AND SUBSTR(TO_CHAR(my_timestamp), 10) < '01:00:00.000'
... But nothing works. Any tips or tricks?
I found a way to do it, but I'd still prefer something a little less hacky, if it exists.
SUBSTR(SUBSTR(TO_CHAR(my_timestamp), 11), 0, 12) > '01.00.00.000'
Your solution looks correct to me except I haven't tried substr function. This is what I used in one of my previous project:
select * from orders
where to_char(my_timestamp,'hh24:mi:ss.FF3')
between '00:00:00.000' and '01:00:00.123';
Use TRUNC(my_timestamp, 'J') to remove the hours and get only the '2013-08-15 00:00:00.00'.
So:
WHERE my_timestamp - TRUNC(my_timestamp, 'J') > 0
AND my_timestamp - TRUNC(my_timestamp, 'J') < 1/24 ;
As a variation on #kubanczyk's answer, since these are timestamps you get an interval when you subtract a value from its truncated form:
select systimestamp - trunc(systimestamp) from dual;
SYSTIMESTAMP-TRUNC(SYSTIMESTAMP)
---------------------------------------------------------------------------
+000000000 09:46:46.589795
Which isn't very helpful. But if you're always looking for exact hours, as in your example, you can extract the hour number from that:
select extract (hour from systimestamp - trunc(systimestamp)) from dual;
EXTRACT(HOURFROMSYSTIMESTAMP-TRUNC(SYSTIMESTAMP))
-------------------------------------------------
9
So in your example you could use:
SELECT *
FROM my_table
WHERE EXTRACT(HOUR FROM my_timestamp - TRUNC(my_timestamp)) = 0
SQL Fiddle demo.
But, this will only be straightforward if the timeslots are exactly aligned with hours; otherwise you'd need to extract other elements too and the logic could get confusing, and #Ankit's approach will be simpler overall.