Oracle order by date gives different results - sql

Just curious as to why the below two queries would give different results, when the testdate column is of datatype DATE.
select testdate from <table> order by to_date(testdate) desc;
Returns
18-DEC-14
17-DEC-14
14-JUL-14
10-JUL-14
.
select testdate from <table> order by testdate desc;
Returns
13-NOV-13
18-DEC-14
17-DEC-14
14-JUL-14
Why would that November 2013 result appear at the top of the second results when as I say the coumn TESTDATE is of data tyle DATE and to_date() resolves the issueX?

Based on OP's reply via comments, the issue is:
SQL> WITH dates(dt) AS(
2 SELECT to_date('18-DEC-2014', 'DD-MON-RR') FROM dual UNION ALL
3 SELECT to_date('17-DEC-2014', 'DD-MON-RR') FROM dual UNION ALL
4 SELECT to_date('14-JUL-2014', 'DD-MON-RR') FROM dual UNION ALL
5 SELECT to_date('10-JUL-2014', 'DD-MON-RR') FROM dual UNION ALL
6 SELECT to_date('13-NOV-3013', 'DD-MON-RR') FROM dual
7 )
8 SELECT dt yy_date,
9 TO_CHAR(dt, 'DD-MON-YYYY') yyyy_date
10 FROM dates
11 ORDER BY dt DESC;
YY_DATE YYYY_DATE
--------- -----------
13-NOV-13 13-NOV-3013
18-DEC-14 18-DEC-2014
17-DEC-14 17-DEC-2014
14-JUL-14 14-JUL-2014
10-JUL-14 10-JUL-2014
SQL>
Th client is displaying the year as YY due to which year 3013 is displayed as 13 and along with other dates 2014, it looked as if 2013 is ordered before 2014 in descending order.
Use TO_CHAR to display the date in your desired format, and use TO_DATE to convert a literal into date.
On a side note,
Never use TO_DATE on a DATE, It will implicitly convert it into string and then back to date using locale-specific NLS format.

TO_DATE function expects string to convert it to date with a given format. Providing date column to to_date may result in unexpected behaviour even oracle does not complain about the given input.

Related

Date difference not matching

I have the below query which gives the below output. The '06/01/20' is some corrupted data I need to deal with. Converting it to 'DD/MM/YY' is not an option, I just would like to understand what's happening here.
WITH aux (
d1,
d2
) AS (
SELECT
'06/01/20',
'15/01/2021'
FROM
dual
)
SELECT
nvl(to_date(d2, 'DD/MM/YYYY'), sysdate) - to_date(d1, 'DD/MM/YYYY') diff
FROM
aux;
Output:
DIFF
----
730862
However, if I do the below, the results do not match with what I would expect, the difference between those dates would be
SELECT
TO_DATE('06/01/20', 'DD/MM/YYYY') d1,
TO_DATE('15/01/2021', 'DD/MM/YYYY') d2
FROM
dual
Output:
D1 D2
---------------------
06-JAN-20 15-JAN-21
--
SELECT
DATE '2021-01-15' - DATE '2020-01-06' d
FROM
dual
Output:
D
-
9
I suggest you set a default date format that allows to see full dates. Many Oracle clients have such settings and you can also change it for current session:
ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD';
When you do so you'll realise that TO_DATE('06/01/20', 'DD/MM/YYYY') produces 0020-01-06 rather than 2020-01-06.
An average year has 365.25 days and 730862 / 365,25 equals 2001 ;-)

BETWEEN Two Dates is Returning Rows of the Same Two Dates instead of the Ones Between

I'm trying to select rows that are between two specific dates, but I'm getting the rows that are of the same dates specified in BETWEEN instead. I tried using operators > and <, but nothing seems to work. Does it have to do with the date format?
SELECT r.RESERVATION_ID, a.AGENT_ID, a.AGENT_FNAME AS AGENT_NAME, t.TRIP_ID,
s.RESERVATION_STATUS
FROM RESERVATION r
INNER JOIN AGENT a
ON
a.AGENT_ID=r.AGENT_ID
INNER JOIN TOURTRIP_RESERVATION t
ON
r.RESERVATION_ID=t.RESERVATION_ID
INNER JOIN RESERVATION_STATUS s
ON
r.RESERVATION_STATUSID=s.RESERVATION_STATUSID
WHERE r.AGENT_ID IS NOT NULL
AND r.RESERVATION_DATE BETWEEN '15-MAR-20' AND '26-MAY-20'
AND r.RESERVATION_STATUSID=100;
I used to_date('03.06.2020','DD.MM.YYYY') format to update the data in the reservation_id column. However, when I use
and RESERVATION_DATE > to_date('15.03.2020','DD.MM.YYYY')
and RESERVATION_DATE < to_date('26.05.2020','DD.MM.YYYY')
it's returning nothing
This is the reservation table
The condition between includes the start and the end.
You can use the operators > and < to exclude it.
Your example simplified:
create table RESERVATION (
RESERVATION_ID number
,RESERVATION_DATE date)
Data:
insert into RESERVATION values (1, to_date('01.06.2020','DD.MM.YYYY'));
insert into RESERVATION values (2, to_date('02.06.2020','DD.MM.YYYY'));
insert into RESERVATION values (3, to_date('03.06.2020','DD.MM.YYYY'));
Query A)
select RESERVATION_ID
from RESERVATION
where RESERVATION_DATE between to_date('01.06.2020','DD.MM.YYYY')
and to_date('03.06.2020','DD.MM.YYYY')
will return:
1
2
3
The Query B)
select RESERVATION_ID
from RESERVATION
where RESERVATION_DATE > to_date('01.06.2020','DD.MM.YYYY')
and RESERVATION_DATE < to_date('03.06.2020','DD.MM.YYYY')
will return
2
I assume that RESERVATION_DATE is a date. Use always explicit datatype conversion.
I changed the date format, try this and tell me if you have other result.
SELECT r.RESERVATION_ID, a.AGENT_ID, a.AGENT_FNAME AS AGENT_NAME, t.TRIP_ID,
s.RESERVATION_STATUS
FROM RESERVATION r
INNER JOIN AGENT a
ON
a.AGENT_ID=r.AGENT_ID
INNER JOIN TOURTRIP_RESERVATION t
ON
r.RESERVATION_ID=t.RESERVATION_ID
INNER JOIN RESERVATION_STATUS s
ON
r.RESERVATION_STATUSID=s.RESERVATION_STATUSID
WHERE r.AGENT_ID IS NOT NULL
AND r.RESERVATION_DATE BETWEEN '2020-03-15' AND '2020-05-26'
AND r.RESERVATION_STATUSID=100;
If RESERVATION_DATE column's datatype is DATE, don't compare it to strings, because '15-MAR-20' is a string. Oracle will try to implicitly convert it to a valid date value; sometimes it'll succeed, sometimes it'll return false value (as its NLS settings differ from what you provided), and sometimes it'll fail and raise an error.
These two:
date '2020-03-15'
to_date('15.03.2020', 'dd.mm.yyyy')
on the other hand, are dates.
BETWEEN is inclusive and will return both limits, if they exist:
AND r.RESERVATION_DATE BETWEEN date '2020-03-15' and date '2020-05-26'
If you want to exclude those limits, then
AND r.RESERVATION_DATE > date '2020-03-15'
AND r.RESERVATION_DATE < date '2020-05-26'
But, if RESERVATION_DATE is a VARCHAR2 column, you're doing a big mistake. Never store dates as strings. If you can't afford modifying datatype, then you'll have to convert it to date:
and to_date(r.reservation_date, 'dd-mon-yy') between date '2020-03-15'
and date '2020-05-26'
This will work as long as there aren't any invalid values in that column. Because, as it is a string, you can put something like 15-AA-2F which certainly isn't a date, but can be stored into such a column. In that case, query will fail and you'll have to fix data.
Comment you posted says that you tried TO_DATE('26-MAY-20'). That's not enough - you should provide format mask as that can be
26th of May 2020
20th of May 2026
and it depends on NLS settings. Furthermore, it'll fail in my database:
Because NLS settings are different:
SQL> select to_date('26-may-20') from dual;
select to_date('26-may-20') from dual
*
ERROR at line 1:
ORA-01858: a non-numeric character was found where a numeric was expected
Because we don't have "may" in Croatia:
SQL> select to_date('26-may-20', 'dd-mon-yy') from dual;
select to_date('26-may-20', 'dd-mon-yy') from dual
*
ERROR at line 1:
ORA-01843: not a valid month
But this works, as I told Oracle what I want:
SQL> select to_date('26-may-20', 'dd-mon-yy', 'nls_date_language = english') from dual;
TO_DATE('26-MAY-20'
-------------------
26.05.2020 00:00:00
Even better, use digits only or date literal (which is always a date 'yyyy-mm-dd'):
SQL> select to_date('26.05.2020', 'dd.mm.yyyy') d1,
2 date '2020-05-26' d2
3 from dual;
D1 D2
------------------- -------------------
26.05.2020 00:00:00 26.05.2020 00:00:00
SQL>
Based on sample data you provided, presuming that reservation_date column's datatype is date:
setting environment first
sample data is from line #1 - 9
query you need begins at line #10
Here you go:
SQL> alter session set nls_date_language = 'english';
Session altered.
SQL> alter session set nls_Date_format = 'dd-mon-yy';
Session altered.
SQL> with reservation (reservation_id, reservation_date, agent_id) as
2 (select 8576, date '2020-03-15', 222 from dual union all
3 select 7325, date '2020-05-26', 333 from dual union all
4 select 3186, date '2020-04-23', 111 from dual union all
5 select 8000, date '2020-04-05', 555 from dual union all
6 select 4120, date '2020-01-03', null from dual union all
7 select 1546, date '2020-02-15', null from dual union all
8 select 1007, date '2020-05-06', null from dual
9 )
10 select *
11 from reservation
12 where reservation_date between date '2020-03-15'
13 and date '2020-05-26'
14 order by reservation_date;
RESERVATION_ID RESERVATI AGENT_ID
-------------- --------- ----------
8576 15-mar-20 222
8000 05-apr-20 555
3186 23-apr-20 111
1007 06-may-20
7325 26-may-20 333
SQL>

Doing Math with Dates given in the problem

The following is the query and code attached to the query.I am not able to figure out how to use the date 31-dec-2006 in the problem.
For each rental property, list the address, include street, city, state.
Also list rental type and number of days listed as "Number of Days Listed". Order results by rental type ascending and number of days listed descending.
Instead of using today's date to determine days listed, use 31-dec-2006
The issue is that Im not receiving any results for this query. I believe I am doing something wrong in the where statement. Im not sure how to assigne a value to the date.
select rp_street, rp_city, rp_state, rp_type, (rp_datelisted - sysdate) as "Number of Days Listed"
from rentproperty
where sysdate = '31-dec-2006'
order by rp_type asc, "Number of Days Listed" desc;
When working with dates, then work with dates, not strings. '31-dec-2006' is just a string. It looks like a date (to us, humans), Oracle will try to convert it to a date (if it can), but you can never be sure it'll work. For example, it won't work in my database:
SQL> select count(*) From emp where hiredate < '31-dec-2006';
select count(*) From emp where hiredate < '31-dec-2006'
*
ERROR at line 1:
ORA-01858: a non-numeric character was found where a numeric was expected
If I fix several things, it will work:
SQL> alter session set nls_date_language = english;
Session altered.
SQL> alter session set nls_date_format = 'dd-mon-yyyy';
Session altered.
SQL> select count(*) From emp where hiredate < '31-dec-2006';
COUNT(*)
----------
14
SQL>
Therefore, either use a date literal (which always looks like date 'yyyy-mm-dd'), or apply the TO_DATE function to a string, with appropriate format mask, e.g. to_date('31.12.2006', 'dd.mm.yyyy') and your query will always work.
Here's what you could have done (I shortened the column list); the RENTPROPERTY CTE lists some sample data; you need code from line 7 onwards.
SQL> with rentproperty (rp_street, rp_type, rp_datelisted) as
2 (select 'Oak street' , 'Type A', date '2000-01-25' from dual union all
3 select '31st street', 'Type B', date '2001-10-30' from dual union all
4 select 'Elm street' , 'Type B', date '2004-08-25' from dual union all
5 select 'Bee street' , 'Type A', date '2006-11-30' from dual
6 )
7 select rp_street,
8 rp_type,
9 (date '2006-12-31' - rp_datelisted) days_listed
10 from rentproperty
11 where rp_datelisted < date '2006-12-31'
12 order by rp_type asc, days_listed desc;
RP_STREET RP_TYP DAYS_LISTED
----------- ------ -----------
Oak street Type A 2532
Bee street Type A 31
31st street Type B 1888
Elm street Type B 858
SQL>
In oracle, sysdate is the current date, so unless today is 31-dec-2006, you'll never get any results. If you've used "sysdate" as the column name, try putting it in quotes.
sysdate = '31-dec-2006' condtion is false that is why no output!!
this is probably what you need:
select rp_street, rp_city, rp_state, rp_type, (rp_datelisted - sysdate) as "Number of
Days Listed"
from rentproperty
where rp_datelisted= '31-dec-2006'
order by rp_type asc, "Number of Days Listed" desc;
The Oracle sysdate returns date and time. Try trunc(sysdate) to compare similar values.
select rp_street, rp_city, rp_state, rp_type, (rp_datelisted - sysdate) as "Number of Days Listed"
from rentproperty
where trunc(sysdate) = '31-dec-2006'
order by rp_type asc, "Number of Days Listed" desc;

How do I generate all the dates of Sunday between 2 dates in oracle sql?

How do I generate all the dates of Sunday between 2 dates in oracle SQL?
Example if I want all the Sundays between "01/10/2018" and "31/12/2018" the output
will be:
07/10/2018
14/10/2018
21/10/2018
...
30/12/2018
Also how do I generate all the dates between 2 dates?
Example: from "01/12/2018" to "31/12/2018"
Output will be:
01/12/2018
02/12/2018
03/12/2018
...
31/12/2018
Here's how; the CTE (dates) creates a "calendar" of all dates starting from 2018-10-01, for number of days between 2018-10-01 and 2018-12-31. This answers your 2nd question.
For the 1st question, using TO_CHAR function with appropriate format mask (dy) and date language (because, if I didn't use it, you'd get Croatian names as that's my default language), select all Sundays.
SQL> with dates as
2 (select date '2018-10-01' + level - 1 datum
3 from dual
4 connect by level <= date '2018-12-31' - date '2018-10-01' + 1
5 )
6 select datum
7 From dates
8 where to_char(datum, 'dy', 'nls_date_language = english') = 'sun';
DATUM
-----------
07-oct-2018
14-oct-2018
21-oct-2018
28-oct-2018
04-nov-2018
11-nov-2018
18-nov-2018
25-nov-2018
02-dec-2018
09-dec-2018
16-dec-2018
23-dec-2018
30-dec-2018
13 rows selected.
SQL>
The following query does it.
First generate rows using connect by clause, each of the row will have the value of "level" column incremented by 1
Also get the day_of_week per date
Filter out the records where day_of_week='sun'
with data
as (
select to_date('1/10/2018','dd/mm/yyyy')+level as sun_day
,to_char(to_date('1/10/2018','dd/mm/yyyy')+level,'dy') day_of_week
from dual
connect by level<=to_date('31/12/2018','dd/mm/yyyy') - to_date('1/10/2018','dd/mm/yyyy')
)
select sun_day
from data
where day_of_week='sun'
For the second part of the query simply remove the filter on the day_of_week
with data
as (
select to_date('1/10/2018','dd/mm/yyyy')+level as sun_day
,to_char(to_date('1/10/2018','dd/mm/yyyy')+level,'dy') day_of_week
from dual
connect by level<=to_date('31/12/2018','dd/mm/yyyy') - to_date('1/10/2018','dd/mm/yyyy')
)
select sun_day
from data;
select
to_char(date,'DD-MON-YYYY')
from
dates
where
(trunc(date) >= '01/12/2018'
and
trunc(date)<= '31/12/2018')
and
to_char(date,'DAY') ='SUNDAY';

convert varchar field data in date format in oracle

I have table test column has cretaed_dt and id.Both are varchar 2 type.
desc test
ID Varchar2(30)
Created_Dt varchar2(30)
select * from test
ID created_dt
1 2014-07-22-12.23.49.832868
2 2014-08-04-19.40.11.787317
3 2014-06-15-19.40.11.787317
I need to pick the data between 21st July to 5th aug 2014.Idealy ID 1 and 2 need to pick.
I am not able to do this because created date column is in varchar2.
Please assist.
Although your data is stored as varchar2(), you can still use it. Happily the strings are in the right format for comparison operations. So:
where created_dte >= '2014-07-21' and created_dte < '2014-08-05'
You can convert the varchar2() to a date using to_date(). However, just using the string as is allows Oracle to take advantage of an index on the column.
Although you can do what you want, you should store date/times in native date format in the database. Oracle has a wealth of date-related functions that are then accessible.
Use this:
with t as (
select '1' id, '2014-07-22-12.23.49.832868' created_dt from dual union all
select '2' id, '2014-08-04-19.40.11.787317' created_dt from dual union all
select '3' id, '2014-06-15-19.40.11.787317' created_dt from dual
)
select *
from t
where to_timestamp(created_dt, 'yyyy-MM-dd-HH24.MI.SS.FF') between to_date('21 jul 2014', 'DD mon YYYY') and to_date('5 aug 2014', 'DD mon YYYY')