不使用日期函式實現ADD_MONTHS函式功能(二)

yangtingkun發表於2008-05-31

看到開發區的nyfor版主出了一道題,用PL/SQL實現OracleADD_MONTHS的功能,覺得比較有意思,忍不住嘗試了一下。

原文如下:http://www.itpub.net/thread-977079-1-1.html

不使用日期函式實現ADD_MONTHS函式功能:http://yangtingkun.itpub.net/post/468/460788

在原有的基礎上做了一點小改動。

 

 

由於這個東西比較耗費精力,因此自從上次的函式實現之後就沒有再花功夫去研究。

不過從其他人那裡學到了一個函式的用法,可以在不改變現有演算法的情況下,再次減少原始碼的長度:

SQL> CREATE OR REPLACE FUNCTION MY_ADD_MONTHS(P_DATE_STRING VARCHAR2, P_MONTHS NUMBER)
  2  RETURN VARCHAR2 AS
  3  SUBTYPE I IS INT;
  4  N I:=P_DATE_STRING;
  5  C I:=100;
  6  D I:=N MOD C;
  7  Y I:=N/C/C;
  8  M I:=N/C MOD C;
  9  Z I:=M+P_MONTHS;
 10  FUNCTION F(Y I,M I) RETURN I
 11  AS
 12  BEGIN
 13  RETURN 31-CASE WHEN M IN(4,6,9,11) THEN 1
 14  WHEN M!=2 THEN 0
 15  WHEN Y MOD 400 = 0 OR Y MOD 4 = 0 AND Y MOD C > 0 THEN 2
 16  ELSE 3
 17  END;
 18  END;
 19  BEGIN
 20  N:=(Z MOD 12 + 11) MOD 12 + 1;
 21  Z:=Y+(Z-N)/12;
 22  IF D=F(Y,M) OR D>F(Z,N) THEN
 23  D:=F(Z,N);
 24  END IF;
 25  RETURN Z*C*C+N*C+D;
 26  END;
 27  /

函式已建立。

SQL> SET SERVEROUT ON
SQL> declare
  2    ln  number;
  3    ld  date;
  4    ls1 varchar2(8);
  5    ls2 varchar2(8);
  6    lt  number := dbms_utility.get_time;
  7    ex exception;
  8    y number;
  9    m number;
 10    d number;
 11    j number;
 12  begin
 13    for j in 0 .. 5000 loop
 14      for y in 2000 .. 2001 loop
 15        for m in 2 .. 4 loop
 16          for d in 28 .. 31 loop
 17            begin
 18              ls1 := y || '0' || m || d;
 19              begin
 20                ld  := to_date(ls1, 'yyyymmdd');
 21              exception
 22                when others then
 23                  exit;
 24              end;
 25              ld  := add_months(ld, j);
 26              ls2 := to_char(ld, 'yyyymmdd');
 27              if nvl(my_add_months(ls1, j), '*') <> ls2 then
 28                dbms_output.put_line('Sorry: stop at p_date_string=' || ls1 ||
 29                                     ',p_months=' || j);
 30                dbms_output.put_line('my_add_months returned: ' ||
 31                                     my_add_months(ls1, j));
 32                dbms_output.put_line('add_months returned: ' || ls2);
 33                raise ex;
 34              end if;
 35           
 36              ls1 := to_char(add_months(ld, -j), 'yyyymmdd');
 37              if nvl(my_add_months(ls2, -j), '*') <> ls1 then
 38                dbms_output.put_line('Sorry: stop at p_date_string=' || ls2 ||
 39                                     ',p_months=' || -j);
 40                dbms_output.put_line('my_add_months returned: ' ||
 41                                     my_add_months(ls2, -j));
 42                dbms_output.put_line('add_months returned: ' || ls1);
 43                raise ex;
 44              end if;
 45            exception
 46              when ex then
 47                raise;
 48              when others then
 49                raise;
 50            end;
 51          end loop;
 52        end loop;
 53      end loop;
 54    end loop;
 55    ln := 0;
 56    for c in (select text
 57                from user_source
 58               where name = 'MY_ADD_MONTHS'
 59                 and type = 'FUNCTION') loop
 60      ln := ln + nvl(lengthb(translate(c.text,
 61                                       '*' || chr(9) || chr(10) || chr(13) ||
 62                                       chr(32),
 63                                       '*')),
 64                     0);
 65    end loop;
 66    lt := (dbms_utility.get_time - lt) / 100;
 67    dbms_output.put_line('Congratulation ... Code Length: ' || ln ||
 68                         ' Bytes. Times: ' ||
 69                         to_char(to_date(to_char(lt, 'fm00000'), 'sssss'),
 70                                 'hh24:mi:ss'));
 71  exception
 72    when ex then
 73      null;
 74  end;
 75  /
Congratulation ... Code Length: 393 Bytes. Times: 00:00:03

PL/SQL 過程已成功完成。

10gPLSQL中,MOD(A,B)可以寫為A MOD B,利用這種方法將原始碼的最終程式碼縮短到了400之內。

不過如果再想縮短就必須修改演算法了。

 

來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/4227/viewspace-327252/,如需轉載,請註明出處,否則將追究法律責任。

相關文章