从年,月和日创建日期

时间:2015-07-21 11:45:58

标签: sql oracle oracle10g oracle-xe

Oracle是否具有内置函数来从其各个组件(年,月和日)创建日期,该日期仅在缺失数据时返回false?

我知道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

4 个答案:

答案 0 :(得分:5)

对于您的示例,您可以使用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没有进行转换的方法,否则返回NULL。 SQL Server最近为此目的引入了try_convert()

一种选择是使用异常处理程序编写自己的函数以进行失败的转换。异常处理程序只会返回NULL格式错误。

答案 1 :(得分:3)

您不能将{0}与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返回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                                

戈登·林诺夫的案例方法更加强大,但如果你真正处理“理智”的积极年份,这可能会奏效。如果没有,那就有点有趣......

答案 2 :(得分:0)

where my_year > 0

这将返回可以创建日期的行。忽略null或值为0的行。如果某些值不在-4713和+9999范围内,那么当然也应该在where子句中排除这些值。

答案 3 :(得分:0)

我最终组成了一个用户定义的函数来封装逻辑。它从其各个组成部分返回日期;如果日期无效,则返回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