问题
Does Oracle have a builtin function to create a date from its individual components (year, month and day) that just returns false on missing data?
I'm aware of TO_DATE()
but I need to compose a string first and neither the ||
operator nor the CONCAT()
function make it easy to handle missing data:
-- my_year NUMBER(4,0) NULL
SELECT TO_DATE(my_year || '-01-01', 'YYYY-MM-DD') AS my_date
FROM my_table;
Whenever my_year
is NULL
we end up with TO_DATE('-01-01', 'YYYY-MM-DD')
and:
ORA-01841: (full) year must be between -4713 and +9999, and not be 0
回答1:
For your example, you can use case
:
select (case when my_year is not null and my_year <> 0 and
my_year between -4713 and 9999
then TO_DATE(my_year || '-01-01', 'YYYY-MM-DD')
end)
Unfortunately, Oracle does not have a method of doing the conversion, if possible, and otherwise returning NULL. SQL Server recently introduced try_convert()
for this purpose.
One option is to write your own function with an exception handler for the failed conversion. The exception handler would simply return NULL
for a bad format.
回答2:
You can't use year zero with to_date('0000-01-01', 'YYYY-MM-DD')
, but oddly you can with a date literal date '0000-01-01'
. On its own that becomes year -1, but you can use it calculations; and you can add an interval too which can be based on your numeric value, e.g.:
SELECT DATE '0000-01-01' + NUMTOYMINTERVAL(my_year, 'YEAR') AS my_date
FROM my_table;
The numtoyminterval function returns null if the argument is null, and adding that to a fixed date also gives you null:
alter session set nls_date_format = 'SYYYY-MM-DD';
select date '0000-01-01' + numtoyminterval(null, 'YEAR') from dual;
DATE'0000-01-01'+NUMTOYMINTERVAL(NULL,'YEAR')
---------------------------------------------
select date '0000-01-01' + numtoyminterval(2015, 'YEAR') from dual;
DATE'0000-01-01'+NUMTOYMINTERVAL(2015,'YEAR')
---------------------------------------------
2015-01-01
select date '0000-01-01' + numtoyminterval(9999, 'YEAR') from dual;
DATE'0000-01-01'+NUMTOYMINTERVAL(9999,'YEAR')
---------------------------------------------
9999-01-01
select date '0000-01-01' + numtoyminterval(-4712, 'YEAR') from dual;
DATE'0000-01-01'+NUMTOYMINTERVAL(-4712,'YEAR')
----------------------------------------------
-4712-01-01
It isn't foolproof; it will still error if you try to go before -4713:
select date '0000-01-01' + numtoyminterval(-4713, 'YEAR') from dual;
SQL Error: ORA-01841: (full) year must be between -4713 and +9999, and not be 0
...
Though you can avoid that with a check constraint on the column. And because of the silent translation of year 0 to -1, you get the same answer if your my_year
value is 0 or -1:
select date '0000-01-01' + numtoyminterval(0, 'YEAR') from dual;
DATE'0000-01-01'+NUMTOYMINTERVAL(0,'YEAR')
------------------------------------------
-0001-01-01
select date '0000-01-01' + numtoyminterval(-1, 'YEAR') from dual;
DATE'0000-01-01'+NUMTOYMINTERVAL(-1,'YEAR')
-------------------------------------------
-0001-01-01
Gordon Linoff's case approach is more robust, but this might work if you're only really dealing with 'sane' positive years. And if not, it's mildly interesting...
回答3:
where my_year > 0
This will just return rows where it's possible to create a date. Ignoring rows with null or with a value of 0. If you have some values not in the range -4713 and +9999, you should of course exclude those in the where clause too.
来源:https://stackoverflow.com/questions/31538180/create-date-from-year-month-and-day