Reputation: 1029
I have following table which records employee's attendance in a company.
CREATE TABLE EMP_ATNDNC
(
WORK_DAY DATE NOT NULL,
EMP_ID VARCHAR2(20) NOT NULL,
STATUS VARCHAR2(1) DEFAULT '0' NOT NULL,
TMSTMP TIMESTAMP,
RCRD_VER TIMESTAMP NOT NULL,
CONSTRAINT EMP_ATNDNC_PK PRIMARY KEY(WORK_DAY,EMP_ID),
CONSTRAINT EMP_ATNDNC_FK_DAY FOREIGN KEY(WORK_DAY) REFERENCES SCHOOL_OPEN_RCRD(WORK_DAY),
CONSTRAINT EMP_ATNDNC_FK_ID FOREIGN KEY(EMP_ID) REFERENCES EMP_RCRD(EMP_ID)
);
As of now, it has two records:
WORK_DAY EMP_ID STATUS TMSTMP RCRD_VER
29-SEP-16 1234 0 27-AUG-16 05.38.46.022000000 PM 27-AUG-16 05.38.46.022000000 PM
26-AUG-16 9999 0 26-AUG-16 04.50.04.628000000 PM 26-AUG-16 04.50.04.628000000 PM
If I run this query:
SELECT * FROM EMP_ATNDNC WHERE WORK_DAY BETWEEN TO_DATE('24-AUG-16') AND TO_DATE('30-SEP-16');
I get only this record:
WORK_DAY EMP_ID STATUS TMSTMP RCRD_VER
29-SEP-16 1234 0 27-AUG-16 05.38.46.022000000 PM 27-AUG-16 05.38.46.022000000 PM
Also If I run this query:
SELECT * FROM EMP_ATNDNC ORDER BY WORK_DAY ASC;
I get this result:
WORK_DAY EMP_ID STATUS TMSTMP RCRD_VER
29-SEP-16 1234 0 27-AUG-16 05.38.46.022000000 PM 27-AUG-16 05.38.46.022000000 PM
26-AUG-16 9999 0 26-AUG-16 04.50.04.628000000 PM 26-AUG-16 04.50.04.628000000 PM
Instead of:
WORK_DAY EMP_ID STATUS TMSTMP RCRD_VER
26-AUG-16 9999 0 26-AUG-16 04.50.04.628000000 PM 26-AUG-16 04.50.04.628000000 PM
29-SEP-16 1234 0 27-AUG-16 05.38.46.022000000 PM 27-AUG-16 05.38.46.022000000 PM
Why is this happening?
Upvotes: 1
Views: 104
Reputation: 191265
You seem to have dates which are not in the current century, possibly through them being inserted with an incorrect format model. (There isn't really any reason to still be using two-digit years any more...)
To demonstrate, with you table definition:
insert into EMP_ATNDNC
values (to_date('26-AUG-16', 'DD-MON-RRRR'), 9999, 0,
to_timestamp('26-AUG-16 04.50.04.628000000 PM', 'DD-MON-RR HH.MI.SS.FF AM'),
to_timestamp('26-AUG-16 04.50.04.628000000 PM', 'DD-MON-RR HH.MI.SS.FF AM'));
insert into EMP_ATNDNC
values (to_date('29-SEP-16', 'DD-MON-YYYY'), 1234, 0,
to_timestamp('27-AUG-16 05.38.46.022000000 PM', 'DD-MON-RR HH.MI.SS.FF AM'),
to_timestamp('27-AUG-16 05.38.46.022000000 PM', 'DD-MON-RR HH.MI.SS.FF AM'));
alter session set nls_date_format = 'DD-MON-RR';
alter session set nls_timestamp_format = 'DD-MON-RR HH:MI:SS.FF3 AM';
SELECT * FROM EMP_ATNDNC WHERE WORK_DAY BETWEEN TO_DATE('24-AUG-16') AND TO_DATE('30-SEP-16');
WORK_DAY EMP_ID S TMSTMP RCRD_VER
--------- -------------------- - ------------------------- -------------------------
26-AUG-16 9999 0 26-AUG-16 04:50:04.628 PM 26-AUG-16 04:50:04.628 PM
WORK_DAY EMP_ID S TMSTMP RCRD_VER
--------- -------------------- - ------------------------- -------------------------
29-SEP-16 1234 0 27-AUG-16 05:38:46.022 PM 27-AUG-16 05:38:46.022 PM
26-AUG-16 9999 0 26-AUG-16 04:50:04.628 PM 26-AUG-16 04:50:04.628 PM
If you look at the two insert statement one is converting the two-digit year using RRRR, which makes some (usually helpful) assumptions about the century, in this case treating '16' as '2016'; the second is using YYYY which is harsher.
If you query the actual dates using a full-year format model you'll see the difference:
select to_char(work_day, 'SYYYY-MM-DD') as work_day, emp_id from emp_atndnc order by work_day;
WORK_DAY EMP_ID
----------- --------------------
0016-09-29 1234
2016-08-26 9999
The problem isn't really with your query (though that is using two-digit years and implicit conversions relying on your session's NLS settings too, which @TimBiegeleisen pointed out isn't a good idea).
The real problem is with the data. You may be able to correct it - if you are also willing to make assumptions, e.g. adding 2000 years to anything before this century.
But really you also need to find out where that bad data came from, and stop it happening. It's likely you have an insert that is relying on implicit date conversion from a string, and different users or applications have different NLS settings - specifically NLS_DATE_FORMAT.
You should never rely on implicit data conversions that rely on NLS settings. And you shouldn't really be using two-digit years any more, though that may be out of your control.
Upvotes: 2
Reputation: 521103
The second date in your BETWEEN
clause has the year specified as a two digit number:
BETWEEN TO_DATE('24-AUG-2016') AND TO_DATE('30-SEP-16');
^^
Assuming that your default date format is dd-mon-yyyy
making the following change might work:
BETWEEN TO_DATE('24-AUG-2016') AND TO_DATE('30-SEP-2016');
However, as the documentation for TO_DATE
mentions:
It is good practice always to specify a format mask (fmt) with TO_DATE
So ideally you should explicitly mention the date format you are using, e.g. as in this query:
SELECT *
FROM EMP_ATNDNC
WHERE WORK_DAY BETWEEN TO_DATE('24-AUG-2016', 'dd-mon-yyyy') AND
TO_DATE('30-SEP-2016', 'dd-mon-yyyy')
Upvotes: 1