如果年份有效,如何将无效日期部分(日/月)替换为默认值01

时间:2017-11-09 23:19:45

标签: sql oracle plsql

如果Year是有效年份,则需要Oracle SQL或存储过程将日期字段中的无效日期或月份转换为01。输入采用varchar格式。

例如,如果输入值为20132016(MMDDYYYY),则Year为有效值2016,现在我必须检查Day和{{1} }。

在上面的示例中,Month也有效Day,但月份无效,值为13。所以我必须将月份转换为01(默认值)。所以预期的输出是20

我需要在我的项目中预测某人的年龄,所以我没有为无效的出生日期插入Null,而是希望保留部分日期值,重要的是年份。

我有一个返回有效日期的函数,但是这个函数没有返回我想要的预期结果。

01132016

3 个答案:

答案 0 :(得分:0)

嵌套测试是一种方式:

CREATE OR REPLACE FUNCTION func_chk_date (p_input IN VARCHAR2)
    RETURN DATE
IS
    v_date   DATE := NULL;
BEGIN
    v_date   := TO_DATE (p_input, 'YYYY/MM/DD');
    RETURN v_date;
EXCEPTION
    WHEN OTHERS
    THEN
        BEGIN
            v_date   := TO_DATE (SUBSTR (p_input, 1, 4), 'YYYY');
            RETURN v_date;
        EXCEPTION
            WHEN OTHERS
            THEN
                RETURN NULL;
        END;
END func_chk_date;

答案 1 :(得分:0)

您可以创建一个功能来捕获您想要尝试更正的日期错误。以下只是一种方式。

create or replace function func_chk_date (date_string_in  varchar2
                                         ,date_format_in  varchar2 default 'mmddyyyy'
                                         )
  return date
  is
      bad_mon_e exception;
      PRAGMA EXCEPTION_INIT(bad_mon_e, -01843);

      bad_day_e exception;
      PRAGMA EXCEPTION_INIT(bad_day_e, -01839);

      bad_mon_day_e exception;
      PRAGMA EXCEPTION_INIT(bad_mon_day_e, -01847);

      date_value_l  date;

      function fix_dd
        return varchar2
      is
      begin
          return substr(date_string_in,1,2) || '01' || substr(date_string_in,5);
      end fix_dd; 

      function fix_mm
        return varchar2
      is
      begin
          return '01' || substr(date_string_in,3);
      end fix_mm;       

  begin
      begin
          date_value_l:= to_date( date_string_in, date_format_in);
      exception
          when bad_mon_e then
                date_value_l:= func_chk_date (date_string_in   => fix_mm
                                             ,date_format_in   => date_format_in
                                             ); 
          when bad_day_e  
            or bad_mon_day_e then 
                date_value_l:=func_chk_date (date_string_in   => fix_dd
                                            ,date_format_in   => date_format_in
                                            ); 

      when others then 
           null;          -- << Actually a very bad idea at least log the error >>         
       end ;
    return date_value_l;
    exception 
        when  others then
            dbms_output.put_line('Other Error:'|| sqlerrm);
  end func_chk_date;
/ 

一些测试数据:

begin 
    dbms_output.put_line ('05052017 returned ' || func_chk_date('05052017'));
    dbms_output.put_line ('02292017 returned ' || func_chk_date('02292017'));
    dbms_output.put_line ('02292016 returned ' || func_chk_date('02292016'));  
    dbms_output.put_line ('13292016 returned ' || func_chk_date('13292016')); 
    dbms_output.put_line ('20402040 returned ' || func_chk_date('20402040'));
    dbms_output.put_line ('0505yyyy returned ' || func_chk_date('0505yyyy'));
end ;

该功能按原样工作,但仅限于日期格式&#34; mmddyyyy&#34;。如果您需要不同的格式,则需要修改fix-mm和/或fix_dd函数。这也是一个递归例程,所以你需要保证有退出路径。

答案 2 :(得分:0)

你的第一个错误就在这里:

V_DATE := TO_DATE( P_INPUT, 'YYYY/MM/DD');

您说预期格式为'MMDDYYYY'(例如&#39; 20132016&#39;)。那么为什么要尝试转换格式为'YYYY/MM/DD'的字符串?

然后,当转换失败时,您甚至都没有尝试检测错误的部分并对其进行修复,而只是返回null。那么这怎么可能会返回所需的日期呢?

让我们从一个算法开始:

  1. 检查输入字符串是否正好是8位数。如果不是,则返回null。
  2. 转换为日期。如果可行,请返回日期。
  3. 否则我们会检查一年。它是零(在Oracle中是不允许的),我们将其设为0001。
  4. 再次转换为日期。如果可行,请返回日期。
  5. 否则我们会检查月份。它是零还是大于12,我们将其设为01。
  6. 再次转换为日期。如果可行,请返回日期。
  7. 否则月份和年份的日期不正确,因此我们将其设为01。
  8. 再次转换为日期并返回日期。
  9. 我在这里使用循环方便:

    CREATE OR REPLACE FUNCTION func_chk_date (p_input IN VARCHAR2)
    RETURN DATE
    IS
      v_date DATE;
      v_datestring CHAR(8);
    BEGIN
      IF NOT REGEXP_LIKE(p_input, '^[[:digit:]]{8}$') THEN
        RETURN NULL;
      END IF;
    
      v_datestring := p_input;
    
      LOOP -- until conversion okay
        BEGIN
          v_date := TO_DATE(v_datestring, 'MMDDYYYY');
          EXIT; -- conversion okay
        EXCEPTION WHEN OTHERS THEN
          IF SUBSTR(v_datestring, 5, 4) = '0000' THEN
            v_datestring := SUBSTR(v_datestring, 1, 4) || '0001';
          ELSIF TO_NUMBER(SUBSTR(v_datestring, 3, 2)) NOT BETWEEN 1 AND 12 THEN
            v_datestring := SUBSTR(v_datestring, 1, 2) || '01' || SUBSTR(v_datestring, 5, 4);
          ELSE
            v_datestring := '01' || SUBSTR(v_datestring, 3, 6);
          END IF;
        END;
      END LOOP;
    
      RETURN V_DATE;
    END FUNC_CHK_DATE;