从年月日创建日期

Create date from year, month and day

Oracle 是否有一个内置函数来从其各个组成部分(年、月和日)创建一个日期,该日期只是 returns 缺少数据时为空?

我知道 TO_DATE() 但我需要先组成一个字符串,|| 运算符和 CONCAT() 函数都无法轻松处理丢失的数据:

-- my_year NUMBER(4,0) NULL
SELECT TO_DATE(my_year || '-01-01', 'YYYY-MM-DD') AS my_date
FROM my_table;

只要 my_yearNULL,我们就会得到 TO_DATE('-01-01', 'YYYY-MM-DD') 和:

ORA-01841: (full) year must be between -4713 and +9999, and not be 0

对于您的示例,您可以使用 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)

不幸的是,如果可能的话,Oracle 没有进行转换的方法,否则 returning NULL。 SQL 服务器最近为此目的引入了 try_convert()

一个选项是为失败的转换编写带有异常处理程序的自己的函数。对于错误的格式,异常处理程序将简单地 return NULL

where my_year > 0

这将只是 return 可以创建日期的行。忽略空值或值为 0 的行。如果您有一些值不在 -4713 和 +9999 范围内,您当然也应该排除 where 子句中的那些值。

你不能在 to_date('0000-01-01', 'YYYY-MM-DD') 中使用零年,但奇怪的是你可以在日期文字 date '0000-01-01' 中使用。单独成为 -1 年,但您可以使用它进行计算;你也可以添加一个间隔,它可以基于你的数值,例如:

SELECT DATE '0000-01-01' + NUMTOYMINTERVAL(my_year, 'YEAR') AS my_date
FROM my_table;

如果参数为 null,则 numtoyminterval function returns null,将其添加到固定日期也会给您 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                                   

这并非万无一失;如果您尝试在 -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
...

虽然您可以通过对列的检查约束来避免这种情况。由于 0 年到 -1 的无声翻译,如果您的 my_year 值为 0 或 -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 的案例方法更可靠,但如果您只真正处理 'sane' 正数年,这可能会奏效。如果不是,那就有点意思了...

我最终编写了一个用户定义的函数来封装逻辑。它 returns 来自其各个组件的日期或 NULL 如果日期无效:

CREATE OR REPLACE FUNCTION TRY_TO_DATE (
    V_YEAR IN NUMBER,
    V_MONTH IN NUMBER,
    V_DAY IN NUMBER
) RETURN DATE DETERMINISTIC IS
BEGIN
    RETURN TO_DATE(LPAD(V_YEAR, 4, '0') || LPAD(V_MONTH, 2, '0') || LPAD(V_DAY, 2, '0'), 'YYYY-MM-DD');
    EXCEPTION
        WHEN OTHERS THEN RETURN NULL;
END TRY_TO_DATE;
/

Fiddle