oracle 从年、月和日创建日期

mbyulnm0  于 2023-04-29  发布在  Oracle
关注(0)|答案(4)|浏览(127)

Oracle是否有一个内置函数,可以从其各个组件(年,月和日)创建一个日期,该日期在缺少数据时返回null?
我知道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
bgibtngc

bgibtngc1#

例如,可以使用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以表示错误的格式。

n1bvdmb6

n1bvdmb62#

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

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
...

不过,您可以通过列上的check约束来避免这种情况。由于年份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

戈登·林诺夫的案例分析方法更有说服力,但如果你只是在处理“理智”的积极年份,这可能会奏效。如果不是,那就有点意思了。..

dl5txlt9

dl5txlt93#

我最终组成了一个用户定义的函数来封装逻辑。它从其各个组件返回一个日期,如果日期无效,则返回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'), 'YYYYMMDD');
    EXCEPTION
        WHEN OTHERS THEN RETURN NULL;
END TRY_TO_DATE;
/

Fiddle

7fhtutme

7fhtutme4#

where my_year > 0

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

相关问题