1

I am not sure if I am looking at this problem the right way: I have a query like this on an Oracle database

SELECT 
    DECODE(
        TO_CHAR(MIN(DATE), 'YYYYMMDD')
        , ''
        , NULL
        , TO_DATE(TO_CHAR(MIN(DATE), 'YYYYMMDD'), 'YYYYMMDD')
    ) 
FROM TABLE 
WHERE id = 76423

which returns 02/03/29. The year should be 1929 and, in fact, that is what I get if I do

SELECT TO_DATE(TO_CHAR(MIN(DATE), 'YYYYMMDD'), 'YYYYMMDD') 
FROM TABLE 
WHERE id = 76423

I don't understand why this should happen, as the date format is clearly specified. Can someone shed some light on this? This is a problem since the date returned by the DECODE query gets read as 02/03/2029 when it is used.

EDIT: The two conversions (from date to char and then again to date) here is due to the fact that the query as I put it here is really two different queries: the first one inserts a DATE in an auxiliary table's column which has a CHAR format; then another query takes this data to the final table, where it's stored as a DATE, and hence the second conversion. I simplified it here to make it more understandable. The DECODE is necessary as some of the rows don't have data on the date column.

Anyway, I think the best solution is to do ALTER SESSION SET NLS_DATE_FORMAT = 'YYYYMMDD' as suggested by @Phil and @LeighRiffel, and then both queries (with and without DECODE) return the same date. Another possible workaround would be to leave the final data as CHAR instead of converting it to DATE, but this would have a large impact on the application I'm working on, so I'm sticking to the ALTER SESSION ... way.

7
  • 1
    In SQL*Plus, do: alter session set NLS_DATE_FORMAT='YYYYMMDD'; then run the first query. Does it show 19 or 20? (Might just be a presentation issue)
    – Philᵀᴹ
    Commented May 23, 2012 at 8:42
  • @Phil Thank you for that. It shows now 1929, so I guess it was just a presentation issue as you said. Anyway, I'm having this problem with dates prior to 1950: after some transformation (which I supppose isn't the one I posted here) they are stored as 20xx.
    – freieschaf
    Commented May 23, 2012 at 9:27
  • 1
    You're probably inserting/transforming using 2 character dates somewhere. A value between 0-49 will become a 20xx year & 50-99 will become a 19xx year. Changing the session NLS_DATE_FORMAT may help. Can you narrow it down to a test case? Edit the question accordingly
    – Philᵀᴹ
    Commented May 23, 2012 at 9:42
  • 1
    Why do you convert the value anyway? select min(date) ... should be enough. If you want a specific output format use select to_char(min(date), 'yyyymmdd') ...
    – user1822
    Commented May 23, 2012 at 9:44
  • 1
    @freieschaf - Could you please clarify your comment? If you are selecting a date from the table and ultimately converting it to a date, why do you need to convert it to and from a character string? Can the application not pass a date from the first query to the second? Commented May 23, 2012 at 12:33

1 Answer 1

2

As a_horse indicated, select min(date)... is sufficient. The DECODE and multiple conversions just slow things down without accomplishing anything. Even as parts of queries, you should eliminate this needless processing.

As Phil indicated, the displaying of dates is controlled by the NLS_DATE_FORMAT parameter and can be altered to display dates as you wish. This does change how the queries run (unless there are missing format specifications or SQL injection is being done). Change the display format like this:

 ALTER SESSION SET NLS_DATE_FORMAT='YYYYMMDD';

When doing a to_date on a two digit year, use YY when you want the first two digits of the year to match the current year, but use RR when you want the first two digits to be 20 for years ending in 0-49 and 19 for years ending in 50-99. See the SQL Language Reference for more information:

The RR datetime format element is similar to the YY datetime format element, but it provides additional flexibility for storing date values in other centuries. The RR datetime format element lets you store 20th century dates in the 21st century by specifying only the last two digits of the year.

If you use the TO_DATE function with the YY datetime format element, then the year returned always has the same first 2 digits as the current year. If you use the RR datetime format element instead, then the century of the return value varies according to the specified two-digit year and the last two digits of the current year.

That is:

  • If the specified two-digit year is 00 to 49, then

    • If the last two digits of the current year are 00 to 49, then the returned year has the same first two digits as the current year.

    • If the last two digits of the current year are 50 to 99, then the first 2 digits of the returned year are 1 greater than the first 2 digits of the current year.

  • If the specified two-digit year is 50 to 99, then

    • If the last two digits of the current year are 00 to 49, then the first 2 digits of the returned year are 1 less than the first 2 digits of the current year.

    • If the last two digits of the current year are 50 to 99, then the returned year has the same first two digits as the current year.

The following examples demonstrate the behavior of the RR datetime format element.

SELECT TO_CHAR(TO_DATE('27-OCT-98', 'DD-MON-RR'), 'YYYY') "Year" FROM DUAL; 
Year
---- 
1998 

SELECT TO_CHAR(TO_DATE('27-OCT-17', 'DD-MON-RR'), 'YYYY') "Year" FROM DUAL; 
Year
----
2017
1
  • Thank you for your detailed post. I explained above the reasons for the conversion and how the ALTER SESSION ... solves the problem.
    – freieschaf
    Commented May 24, 2012 at 9:53

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.