Oracle SQL : timestamps in where clause - sql

I need to look up rows within a particular time frame.
select *
from TableA
where startdate >= '12-01-2012 21:24:00'
and startdate <= '12-01-2012 21:25:33'
I.e.: I need to look up rows with timestamp precision of SECONDS. How do I achieve this?
FYI: The startdate column is of type TIMESTAMP.

to_timestamp()
You need to use to_timestamp() to convert your string to a proper timestamp value:
to_timestamp('12-01-2012 21:24:00', 'dd-mm-yyyy hh24:mi:ss')
to_date()
If your column is of type DATE (which also supports seconds), you need to use to_date()
to_date('12-01-2012 21:24:00', 'dd-mm-yyyy hh24:mi:ss')
Example
To get this into a where condition use the following:
select *
from TableA
where startdate >= to_timestamp('12-01-2012 21:24:00', 'dd-mm-yyyy hh24:mi:ss')
and startdate <= to_timestamp('12-01-2012 21:25:33', 'dd-mm-yyyy hh24:mi:ss')
Note
You never need to use to_timestamp() on a column that is of type timestamp.

For everyone coming to this thread with fractional seconds in your timestamp use:
to_timestamp('2018-11-03 12:35:20.419000', 'YYYY-MM-DD HH24:MI:SS.FF')

Related

Get date and time from date column in oracle

I inserted date and time in database, now when trying to retrieve date and time both from database only getting time part.
I tried insert date using TO_DATE('08/13/2019 09:10:03', 'MM/DD/YYYY HH24:MI:SS') into 'Time' col. Now trying to get date from table using TO_DATE(Time, 'DD/MON/RR HH24:MI:SS'), but only getting date part. My database nls date format is "DD/MON/RR". 'Time' col is date type and I'm using oracle 10g xe.
I can get date and time using TO_CHAR(Time, 'DD/MON/RR HH24:MI:SS') but as I need to use this in comparison operation like below:
select TO_CHAR(Time,'DD/MON/RR HH24:MI:SS') from Table where (TO_CHAR(Time, 'DD/MON/RR HH24:MI:SS') <= TO_DATE('08/07/2019 10:13:52', 'MM/DD/YYYY HH24:MI:SS'))
it gives this error 'ORA-01830: date format picture ends before converting entire input string'. Also tried to use TO_DATE(TO_CHAR(Time, 'DD/MON/RR HH24:MI:SS')), still it gives only time part. Should I use TIMESTAMP datatype for 'Time' col?
I want to get date and time from table where i can use them in comparison operation.
Date need not be converted into date again.
You can simply write your query like this:
SELECT
TIME -- use to_char for formatting the output date
FROM Table
WHERE
TIME <= TO_DATE('08/07/2019 10:13:52', 'MM/DD/YYYY HH24:MI:SS')
Cheers!!

Postgres date issue

I am trying to select records dated 1st of December 2018 from a table.
Even though there are several records postgres is not returning any.
Query:
select *
from dbo."Transactions"
where "DateOfTransaction"::timestamp >=to_date('01-12-2018', 'dd-mm-yyyy')
and "DateOfTransaction"::timestamp <=to_date('01-12-2018', 'dd-mm-yyyy')
I also tried:
select *
from dbo."Transactions"
where "DateOfTransaction"::timestamp >=to_date('01-12-2018 00:00:00', 'dd-mm-yyyy HH24:MI:SS')
and "DateOfTransaction"::timestamp <=to_date('01-12-2018 23:59:59', 'dd-mm-yyyy HH24:MI:SS')
What is the reason for this strange behavior?
I have to give date in dd-mm-yyyy format in where condition.
As an explanation on why your query isn't working:
to_date('01-12-2018', 'dd-mm-yyyy') creates a date value (even if you specify a time part), so when you compare that with a timestamp it is as if you were comparing that with a timestamp value where the time part is set to 00:00:00.
So your first query is essentially the same as:
where "DateOfTransaction" >= timestamp '2018-12-01 00:00:00'
and "DateOfTransaction" <= timestamp '2018-12-01 00:00:00'
Which would only return rows that contain exactly 2018-12-01 00:00:00
How to do it correctly?
You could simply cast the timestamp to a date and compare it with =
where "DateOfTransaction"::date = date '2018-12-01'
which would not be able to use an index on "DateOfTransaction"
If you need to make sure that query uses an index, you can use:
where "DateOfTransaction" >= DATE '2018-12-01'
and "DateOfTransaction" < DATE '2018-12-01' + 1
Note that the cast "DateOfTransaction"::timestamp is useless as the column is already a timestamp

Oracle Convert Char to Date

I have the following date in oracle table:
'2017-08-01 00:00:00,000000000'
I want to convert this to date which I am using the following but I don't know how to deal with zeroes?!
.
.
.
T.EXECUTION_LOCAL_DATE_TIME
between to_date('2017-08-01 00:00:00,000000000', 'yyyy-mm-dd hh:mi:ss')
and to_date('2017-08-10 00:00:00,000000000', 'yyyy-mm-dd hh:mi:ss');
Could anyone help me with this?
Oracle dates do not support milliseconds. Assuming your EXECUTION_LOCAL_DATE_TIME column is a date, then the following comparison is the best you can do:
T.EXECUTION_LOCAL_DATE_TIME
BETWEEN TO_DATE('2017-08-01 00:00:00', 'yyyy-mm-dd hh:mi:ss') AND
TO_DATE('2017-08-10 00:00:00', 'yyyy-mm-dd hh:mi:ss');
If you wanted to convert a text string with milliseconds to a timestamp, you could use something like this:
TO_TIMESTAMP ('2017-08-01 00:00:00,000000000', 'yyyy-mm-dd hh:mi:ss,ff')
Generally speaking this values in not a date but a timestamp so I would use following conversion:
select to_timestamp('2017-08-01 00:00:00,000000000', 'yyyy-mm-dd hh24:mi:ss,ff6')
from dual;
Technically you can have non zeros after comma, so If you want to get correct but not truncated value use timestamp date type.

Compare timestamps in Oracle

I have a column which is used to store date and time data in a VARCHAR2 column in my DB in Oracle 11g Express in the format of:
9/30/2016 14:00:00
I was trying out ways to get data between time ranges. I found the following 2 ways:
select *
from dummy
WHERE starttime > '9/30/2016 14:00:00'
AND starttime < '9/30/2016 17:00:00'
order by starttime;
select *
from dummy
WHERE to_timestamp(starttime, 'mm/dd/yyyy hh24:mi:ss') > TO_TIMESTAMP('9/30/2016 14:00:00', 'mm/dd/yyyy hh24:mi:ss')
AND TO_TIMESTAMP(starttime, 'mm/dd/yyyy hh24:mi:ss') < TO_TIMESTAMP('9/30/2016 17:00:00', 'mm/dd/yyyy hh24:mi:ss');
I was wondering how the first method works too as the column starttime is stored in VARCHAR format and without converting to a Timestamp the comparison still works. Could someone explain to me how/ why that happens or if there is some corner case for which it will not work? Thanks.
This works because you have no issue with months and years or one-digit vs. two-digit days. Think of any strings that are inside the range '9/30/2016 14:00:00' to '9/30/2016 17:00:00'. They will all have to start with '9/30/2016 1'.
If the range where, say '9/30/2016 14:00:00' to '10/30/2016 17:00:00', you wouldn't find any record at all, because the string would have to start with somthing >= '9' and <= '1' which is not possible.
So it is the narrow range within a particular day that saved you here :-)
Storing the values in a VARCHAR column means that you will do a string comparison:
SELECT *
FROM dummy
WHERE starttime > '9/30/2016 14:00:00'
AND starttime < '10/30/2016 17:00:00'
ORDER BY starttime;
This would look at the start time and consider it character-by-character and if the 1st character is greater than '9' and also less than '1' then it will return a row (since this will never be true it will not return a row). Moreover, it will not consider that the 9 and the 10 represent the months and that when doing a string comparison '09/30/2016' < '09/31/1900' < '10/30/2016'.
Even if you store the value in a TIMESTAMP column, using string literals is a bad idea:
SELECT *
FROM dummy
WHERE starttime > '9/30/2016 14:00:00'
AND starttime < '9/30/2016 17:00:00'
ORDER BY starttime;
This works as Oracle will perform an implicit cast (using TO_TIMESTAMP( time, format_mask )) using the session parameter NLS_TIMESTAMP_FORMAT as the format mask.
So your query would (assuming a TIMESTAMP data type) effectively be (although Oracle will implement it in a more efficient fashion):
SELECT *
FROM dummy
CROSS JOIN
( SELECT value AS format_mask
FROM NLS_SESSION_PARAMETERS
WHERE PARAMETER = 'NLS_TIMESTAMP_FORMAT' ) nls
WHERE starttime > TO_TIMESTAMP( '9/30/2016 14:00:00', nls.format_mask )
AND starttime < TO_TIMESTAMP( '9/30/2016 17:00:00', nls.format_mask )
ORDER BY starttime;
The NLS_TIMESTAMP_FORMAT is a session parameter - this means that each user can set their own value for this parameter in their own session and if one user changes it to YYYY-MM-DD"T"HH24:MI:SS.ZZZ"Z" (i.e. an ISO8601 format) then your query will break for that user (and not for the other users who have not changed it) without any changes having been made to your query.
Rather than using a string literal and implicit conversion, it is better to either explicitly set the format mask you are expecting or to use an ANSI TIMESTAMP literal:
SELECT *
FROM dummy
WHERE TO_TIMESTAMP( starttime, 'MM-DD-YYYY HH24:MI:SS' ) > TIMESTAMP '2016-09-30 14:00:00'
AND TO_TIMESTAMP( starttime, 'MM-DD-YYYY HH24:MI:SS' ) < TIMESTAMP '2016-09-30 17:00:00'
ORDER BY TO_TIMESTAMP( starttime, 'MM-DD-YYYY HH24:MI:SS' );
You would then benefit from a function-based index on TO_TIMESTAMP( starttime, 'MM-DD-YYYY HH24:MI:SS' ).
Even better, would be to convert your column to the correct TIMESTAMP format then you do not need a function-based index and can just use TIMESTAMP literals for the bounds without any need for conversion functions.
Storing date as varchar is less than clever...
Your first method is fine, provided you don't need to cross the boundary of a year. The numbers are compared left to right (because text). Unless you store as 'YYYY-MM-DD HH24:MI:SS' you will run into problems.
2 options, change that storage to DATE, or use a to_date or to_timestamp on the WHERE clause (I recommend to_date)
It's a bad idea to store date/times as character strings or numbers. The optimizer has no idea of the domain and so when attempting to estimate the cardinality you are not giving the optimizer the best chance. For example, consider the following two dates
Dec 31st 2016
Jan 1st 2017
If you store these as a number, you might use
20170101 and 20161231
So what is the number of days between them? Using numbers, you get
20170101 - 20161231
= 8870
However, the true (date based) answer is one.
Although you can TO_DATE() or CAST your columns, you now run the risk of not being able to use certain optimizations, such as indexing, partition pruning, bloom filtering etc.
So I highly recommend using the correct data types.
On Oracle, you could use too:
SELECT * FROM table
WHERE field BETWEEN TRUNC(SYSDATE - 6) AND SYSDATE

oracle between clause inclusive format HH24:MI

My query looks something like:
select *
from mytable
where date_field between to_date(#from#, 'YYYY/MM/DD HH24:MI')
and to_date(#to#, 'YYYY/MM/DD HH24:MI')
As an example:
if from = 2012/07/18 00:00 and
to = 2012/07/18 00:09
will this include records with timestamp 2012/07/18 00:09:01 to 2012/07/18 00:09:59?
or should I change the statement to:
select *
from mytable
where date_field >= to_date(#from#, 'YYYY/MM/DD HH24:MI')
< to_date(#to#, 'YYYY/MM/DD HH24:MI')
here substituting from : 2012/07/18 00:00 & to: 2012/07/18 00:10 should give me all records with timestamp between midnight & 9M59S past midnight, which is what I want.
The between clause accepts both the interval bounds.
I suggest the second option to you
select *
from mytable
where date_field >= to_date(#from#, 'YYYY/MM/DD HH24:MI')
< to_date(#to#, 'YYYY/MM/DD HH24:MI')
You may find this article interesting.
The date conversion is going to convert the values into dates, which contain all date elements. You have not specified seconds in the strings, so these will become 0.
In other words, the range ":01" - ":59" is not included.
Since you are working with strings and the strings have date elements in the proper order for comparison, why not do string compares instead:
where to_char(datefield, 'YYYY/MM/DD HH24:MI') between #from# and #to#
I think this does exactly what you want, without fiddling around with date arithmetic.
You can also change the statement as you propose, by incrementing the #to# column and using "<" instead of between.
You could do something like this:
SELECT *
FROM mytable
WHERE date_field between to_date(#from#, 'YYYY/MM/DD HH24:MI')
AND to_date(#to#||':59', 'YYYY/MM/DD HH24:MI:SS')
Ignoring the date portion of the DATE elements, since both 00:09:01 and 00:09:59 come after your "to" time of 00:09:00, no, this query will not include those records.
If you want to include those records, you will need to extend your "to" time to 00:10:00 or TRUNC your records's timestamps to the nearest minute.
Edit:
If your from and to are only accurate to the minute, I'd do this:
SELECT *
FROM mytable
WHERE date_field >= to_date(#from#, 'YYYY/MM/DD HH24:MI')
AND date_field < to_date(#to#, 'YYYY/MM/DD HH24:MI') + 1/24/60/60 /* 1 minute */
And make sure you use bind variable for from and to. Is this ColdFusion? If so, use cfqueryparam.