Postgres date issue - sql

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

Related

how to filter a db for datetime in sqldeveloper

I need to filter a table for specific, accurate to the second, timeperiods.
Datatype is "TIMESTAMP (6)"
select *
from table
where trunc(load_date)<=to_date ('10.08.15 15:10:58', 'dd.mm.yy hh24:mi:ss')
and trunc(load_date)>=to_date ('10.08.15 15:11:08', 'dd.mm.yy hh24:mi:ss');
This is what if come to. But i seem to miss smt.
select *
from b_bis_donexa_delta_2
where trunc(load_date)=to_date ('10.08.15', 'dd.mm.yy');
This works perfectly fine. But scolling through the results isn't very efficient.
atm i get no results, but no error message.
but there IS at least one result. i even tried to swap those < = > randomly because i thought i lost my mind.
Doing trunc(load_date) truncates the time to midnight on that value's day (and also converts to a date, rather than a timestamp):
select systimestamp, trunc(systimestamp) from dual;
SYSTIMESTAMP TRUNC(SYSTIMESTAMP)
------------------------------------ -------------------
2019-09-10 12:53:54.400453000 +01:00 2019-09-10 00:00:00
Once you've truncated your load_date, that midnight time is not within your target range. In your second version you are comparing the truncated value with a time which is also at midnight, hence it now finding a match - but it may or may not be in your 10-second window (there's no way to tell), and also may prevent an index on that column being used - which is probably why it's slow.
Don't truncate; and I'd compare against the same data type:
select *
from table
where load_date >= to_timestamp ('10.08.15 15:10:58', 'dd.mm.yy hh24:mi:ss')
and load_date <= to_timestamp ('10.08.15 15:11:08', 'dd.mm.yy hh24:mi:ss');
or preferably using 4-digit years:
select *
from table
where load_date >= to_timestamp ('10.08.2015 15:10:58', 'dd.mm.yyyy hh24:mi:ss')
and load_date <= to_timestamp ('10.08.2015 15:11:08', 'dd.mm.yyyy hh24:mi:ss');
or even timestamp literals if these are fixed values:
select *
from table
where load_date >= timestamp '2015-08-10 15:10:58'
and load_date <= timestamp '2015-08-10 15:11:08';
Also check that you do really want both >= and <=; if you are getting multiple 10-second ranges then you may actually want >= and < to avoid the exact time appearing in two ranges.

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 to_date returns some error in custom query

Oracle DB
START_DATE |END_DATE
--------------------|-------------------
2016-02-01 00:00:00 |2016-02-29 23:55:00
2016-02-01 00:00:00 |2016-02-29 23:55:00
2016-02-01 00:00:00 |2016-02-29 23:55:00
2016-02-01 00:00:00 |2016-02-29 23:55:00
2016-02-01 00:00:00 |2016-02-29 23:55:00
Query
`select * from VM_REPORT_TEMP_US where to_date(START_DATE,'YYYY-MM-DD HH24:MI:SS') >= '2016-02-01 00:00:00' AND to_date(END_DATE,'YYYY-MM-DD HH24:MI:SS') <= '2016-`02-24 24:59:00'`
Im trying to run this query but im getting some error. Can some one know where im going in this query ?
Im getting the below error
SQL Error [1861] [22008]: ORA-01861: literal does not match format string
java.sql.SQLDataException: ORA-01861: literal does not match format string
Do NOT use to_date() with a DATE column. That first converts the date value into a varchar value just to convert that back to a date which it was to begin with.
to_date() expects a varchar value, so Oracle first converts the DATE value into a varchar value using the current NLS settings. Then it tries to convert that varchar back into a date, using the format mask you supplied which most probably doesn't match the default NLS format you have and therefor you get an error.
You should also use proper date values in the condition rather then strings that are (again) implicitly converted to a DATE based on the current NLS settings:
select *
from VM_REPORT_TEMP_US
where START_DATE >= timestamp '2016-02-01 00:00:00'
AND END_DATE <= timestamp '2016-02-24 23:59:00'
Note that the hour 24 is invalid in an ISO timestamp literal.
If you want to provide the date/timestamp value in a format other then the ISO format, you need to use to_date() for those:
select *
from VM_REPORT_TEMP_US
where START_DATE >= to_date('01.02.2016 00:00:00', 'dd.mm.yyyy hh24:mi:ss')
AND END_DATE <= to_date('24.02.2016 23:59:59', 'dd.mm.yyyy hh24:mi:ss')
start_date and end_date are dates already, so you don't have to convert them to date. Convert the strings instead or even better use datetime literals.
select *
from VM_REPORT_TEMP_US
where START_DATE >= timestamp '2016-02-01 00:00:00'
and END_DATE <= timestamp '2016-02-24 00:59:00'
Bit different from both the above answers, but this is what I would use to avoid confusion between YYYY-MM-DD and YYYY-DD-MM.
select * from VM_REPORT_TEMP_US
where
START_DATE >= to_date('2016-02-01 00:00:00','YYYY-MM-DD HH24:MI:SS')
AND
END_DATE <= to_date('2016-02-24 00:59:00','YYYY-MM-DD HH24:MI:SS')

Date range problems in Oracle. How do I correctly grab date ranges in this format?

I have this Oracle database, and it has a list of dates in this format: mm/dd/yy 0:00:00 AM/PM (1 for January, instead of 01; 1 for the day, instead of 01, and so forth).
When I try a query such as:
select DATELIST from CORE.DATE_TEST
where to_date(DATELIST) >= to_date('8/27/2015', 'mm/dd/yy') AND to_date(DATELIST, 'mm/dd/yy') <= to_date('9/27/2015', 'mm/dd/yy');
It gives me the following error: ORA-01843: not a valid month. I can't see how 27 is a valid month either, but it will accept that input if I try
When I try to change it to the expected date formats:
m/dd/yy
mm/dd/yyyy
m/d/yy
I get this error: ORA-01821: date format not recognized
When I switch it up to dd/mm/yy, it sort of works, but not really; it gives me invalid results.
However, this works:
select DATELIST from CORE.DATE_TEST
where to_date(DATELIST) >= to_date('8/27/2015', 'mm/dd/yy')
The PL/SQL script is breaking on the following addition:
AND to_date(DATELIST, 'mm/dd/yy') <= to_date('9/27/2015', 'mm/dd/yy');
When I replace the above code with this:
SELECT to_date(DATELIST, 'dd/mm/yy')
FROM CORE.DATE_TEST
WHERE DATELIST >= to_date('8/27/2015', 'mm/dd/yy')
AND DATELIST <= to_date('9/27/2015', 'mm/dd/yy');
It works, and is a million times faster, but it won't correctly parse the date range. The lesser-than-or-equal-to (<=) operator is only grabbing data between 8/27/2015, and 9/26/2015. Isn't it supposed to get 8/27/2015 to 9/27/2015?
So I change the last line to this:
AND DATELIST <= to_date('9/27/2015', 'mm/dd/yy') + 1
...and it works, but it doesn't seem like I'm handling this right.
What am I doing wrong? Also, if there's a better, more efficient way of doing this, I'm all ears.
You have a number of issues in your original question, I'll try to address some or all of them here:
When I try a query such as:
select DATELIST from CORE.DATE_TEST
where to_date(DATELIST) >= to_date('8/27/2015', 'mm/dd/yy') AND to_date(DATELIST, 'mm/dd/yy') <= to_date('9/27/2015', 'mm/dd/yy');
It gives me the following error: ORA-01843: not a valid month
You are not specifying a date format when you specify to_date(DATELIST) therefore it will default to the NLS setting for the default date format. You should always specify a date format whenever you use TO_DATE to prevent this from happening and ensure your date formats are explicit (and you are using yy and yet you are specifying a 4 digit year so you should use yyyy).
Try this instead (assuming DATELIST is a DATE datatype):
select TO_CHAR(DATELIST, 'DD-MON-YYYY HH24:MI:SS') -- Use whatever date format is clearest to you
from CORE.DATE_TEST
where DATELIST >= to_date('8/27/2015 00:00:00', 'mm/dd/yyyy hh24:mi:ss') AND DATELIST <= to_date('9/27/2015 23:59:59', 'mm/dd/yyyy hh24:mi:ss');
If DATELIST is a VARCHAR2 then you'll need to see what date format the values are stored in within the column and format the query accordingly.
The lesser-than-or-equal-to (<=) operator is only grabbing data
between 8/27/2015, and 9/26/2015. Isn't it supposed to get 8/27/2015
to 9/27/2015?
When you use TO_DATE and don't specify a time portion of the date, it defaults to 00:00:00 for the hours, minutes and seconds. If you want the full date range try to be more explicit:
SELECT to_date(DATELIST, 'dd/mm/yy hh24:mi:ss')
FROM CORE.DATE_TEST
WHERE DATELIST >= to_date('8/27/2015 00:00:00', 'mm/dd/yyyy hh24:mi:ss')
AND DATELIST <= to_date('9/27/2015 23:59:59', 'mm/dd/yyyy hh24:mi:ss');
The reason the query is much faster is, I suspect, that you have an index on the DATELIST column which you are defeating when you wrap it with a TO_DATE call such as TO_DATE(DATELIST, 'mm/dd/yyyy').
If the datatype of DATELIST is DATE then leave it as such, if it's VARCHAR2 then you have some additional date formatting issues.
Hope it helps
EDIT:
I think your issue is to do with the hidden time portions of the DATE datatype DATELIST.
If you have the value:
09/27/2015 10:35:12
Even if you don't care about the 10:35:12 portion of that date, it is still there. If you then try to use the following where clause:
WHERE DATELIST >= TO_DATE('09/26/2015', 'MM/DD/RRRR')
AND DATELIST <= TO_DATE('09/27/2015', 'MM/DD/RRRR');
Then the value won't be picked up as it is greater than:
09/27/2015 00:00:00.
You need to either up the higher bound by a day or specify the time portion e.g.:
WHERE DATELIST >= TO_DATE('09/26/2015 00:00:00', 'MM/DD/RRRR HH24:MI:SS')
AND DATELIST <= TO_DATE('09/27/2015 23:59:59', 'MM/DD/RRRR HH24:MI:SS');
EDIT2:
Working with the OP, he has said that this has worked:
SELECT to_date(DATELIST, 'dd/mm/yy') as Date_Accessed
FROM CORE.DATE_TEST
WHERE (trunc(DATELIST) >= to_date('9/27/2015', 'mm/dd/yy'))
AND (trunc(DATELIST) <= to_date('9/30/2015', 'mm/dd/yy'));
Though this will defeat any index on the DATELIST column.
Instead of using to_date use the ANSI date literal:
DATE '2015-09-27'
From oracle documentation:
The ANSI date literal contains no time portion, and must be specified
in exactly this format ('YYYY-MM-DD').

Oracle SQL : timestamps in where clause

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')