不使用日期函数实现ADD_MONTHS函数功能
上一篇 / 下一篇 2008-04-28 23:56:20 / 个人分类:ORACLE
看到开发区的nyfor版主出了一道题,用PL/SQL实现Oracle的ADD_MONTHS的功能,觉得比较有意思,忍不住尝试了一下。
原文如下:http://www.itpub.net/thread-977079-1-1.html
要求不能使用任何日期函数,不能使用SQL,不能使用Oracle提供的标准包以外的包,只通过标准函数来实现这个功能,且要求源代码尽量短。
经过多次尝试和努力,将字符控制到了420之内,下面贴出这个函数的实现:
SQL> CREATE OR REPLACE FUNCTION MY_ADD_MONTHS(P_DATE_STRING VARCHAR2, P_MONTHS NUMBER)ITPUB个人空间9@;an$T+O-H
2 RETURN VARCHAR2 ASITPUB个人空间3u.LA].I+A7\P'@`x
3 SUBTYPE I IS INT;
!DqaU^kp0 4 N I:=P_DATE_STRING;ITPUB个人空间 }Et7l&n1\%Z
5 C I:=100;
:LKdp9PUq-z0 6 D I:=MOD(N,C);
+K:w:P2C'K/Q3\y
I4M[0 7 Y I:=N/C/C;ITPUB个人空间LTo6D
PMBM5k
8 M I:=MOD(N/C,C);
~:eO-}_0h0 9 Z I:=M+P_MONTHS;
^HW W'C0 10 FUNCTION F(Y I,M I) RETURN IITPUB个人空间$fq%_-B@,|+RptI)p
11 ASITPUB个人空间K"dBOVo
12 BEGINITPUB个人空间7}!GI)\Cs TH
13 RETURN 31-CASE WHEN M IN(4,6,9,11) THEN 1ITPUB个人空间1Y#dW Sar"^
14 WHEN M!=2 THEN 0
e2\QMFMiu8s0 15 WHEN MOD(Y,4*C)=0 OR MOD(Y,4)=0 AND MOD(Y,C)>0 THEN 2ITPUB个人空间3vDl B2MHkq8\
16 ELSE 3
9c|1N o.lVL0 17 END;
-x({;l`-R0 18 END;ITPUB个人空间3p mVP%B ^
19 BEGIN
\qa4p _8h'T0 20 N:=MOD(MOD(Z,12)+11,12)+1;
'sX(W0r/lO @+~[0 21 Z:=Y+(Z-N)/12;ITPUB个人空间6oW0g F\1dv$O
22 IF D=F(Y,M) OR D>F(Z,N) THENITPUB个人空间I8S:` |'K9o3f
23 D:=F(Z,N);ITPUB个人空间n{Zqs.S}.y
24 END IF;ITPUB个人空间+Qd^k-}3|
25 RETURN Z*C*C+N*C+D;ITPUB个人空间]%w ^bt\D"u
26 END;ITPUB个人空间2}QBXq'|y,i#c
27 /
函数已创建。
虽然这个实现不是最短的,但是这个函数中从头到尾只用了一个函数MOD。
下面利用nyfor版主提供的测试代码进行测试:
SQL> SET SERVEROUT ON
mX#Uv&G6b3@0SQL> declare
4K P!cGQQ4lnd0 2 ln number;ITPUB个人空间
` Bm]:@6u!pUx
3 ld1 date;
(jr6bR#fF9X4n#`u0 4 ld2 date;ITPUB个人空间vX!xIHQ
5 ls varchar2(8);ITPUB个人空间pF^F&D"j;V
6 lt number := dbms_utility.get_time;
Bf2L8N[I9I9k0 7 ex exception;
`6S;W{_p1u0 8 i number;ITPUB个人空间\Wi+~])p
9 j number;
C,P$IoV#xw0 10 beginITPUB个人空间%KD-s-Qe,yFU
W s
11 ld1 := to_date('20080101', 'yyyymmdd');
jVh n;u F0 12 i := 0;
GssO@h5vM0 13 loop
u
}Hw/B
n;Id$i0 14 exit when i > 800;
c_W
ne ZTh+Q-\c9Z0 15 j := -2400;
W XdD2`8G!Du0 16 loopITPUB个人空间,\N5m)n`Dg
17 exit when j > 2400;ITPUB个人空间6J9N
W$DK5E3Z-Q
18 if to_char(ld1 + i,'dd')= '02' then
'YW#P7~${c/V"yIYe.L0 19 i := i + 25;ITPUB个人空间+W%BbEb8|)ht
20 end if;
"k2[X
z%yj8un3A0 21 if to_char(add_months(ld1 + i, j), 'yyyymmdd') <>
W!`v%ZhW%sp3o&n0 22 nvl(my_add_months(to_char(ld1 + i, 'yyyymmdd'), j), '*') then
8@9hgT9ZZ%~0 23 dbms_output.put_line('Sorry: stop at p_date_string=' ||ITPUB个人空间"r:P;k-]`n)v9g
24 to_char(ld1 + i, 'yyyymmdd') || ',p_months=' || j);ITPUB个人空间5J'vd$Lc4M&EO@
a H
N
25 dbms_output.put_line('my_add_months returned: '||my_add_months(to_char(ld1 + i, 'yyyymmdd'), j) ||
s%X,Hb8O/_m0 26 ', add_months returned: ' || to_char(add_months(ld1 + i, j), 'yyyymmdd'));ITPUB个人空间 nzsU0{n;AV
27 raise ex;ITPUB个人空间;Z,^!XR{ F2~.K"g
R
28 end if;
!`h
}d*A`0 29 j := j + 1;ITPUB个人空间$}*thp-h
30 end loop;ITPUB个人空间8oM8V Qd+?
31 i := i + 1;
Y;M3N)ra0 32 end loop;
"Z
p F mz7H%V1[0 33 ln := 0;
SU*`9B X0 34 for c in (select text
E%?8gNF)MPf3s0 35 from user_source
*i8}S'BxTcz0 36 where name = 'MY_ADD_MONTHS'ITPUB个人空间2V1?e+sVOd
37 and type = 'FUNCTION') loopITPUB个人空间"c6f:[uo)T4uN
38 ln := ln + nvl(lengthb(translate(c.text,ITPUB个人空间a b,D+KI*N%b \|
39 '*' || chr(9) || chr(10) || chr(13) ||
E6f.ishBD0 40 chr(32),ITPUB个人空间+`oMb6h,N1r
41 '*')),
Sz.fi9[m{A0 42 0);
|2??e8CV'@'u(_0 43 end loop;
q1x
~r/W0 44
C+h }zQ ZTU!B0 45 lt := (dbms_utility.get_time - lt) / 100;ITPUB个人空间eeA/e8zY _HrS?
46 dbms_output.put_line('Congratulation ... Code Length: ' || ln ||ITPUB个人空间"w$n%^w.G$w5`1s
47 ' Bytes. Times: ' ||ITPUB个人空间 ?mT?6F }j"E
48 to_char(to_date(to_char(lt, 'fm00000'), 'sssss'),ITPUB个人空间2P+W9z8{bm
49 'hh24:mi:ss'));
;Y"m)xY*j@6n0 50 exception
%SUEs9j;n4F;z0 51 when ex thenITPUB个人空间:AoW3xo kl"k
52 null;ITPUB个人空间!o[3l2pD9za9E|1r$m
53 end;
0[cC!l*L4p*z0 54 /ITPUB个人空间%u7eq-L7@H2l6na
Congratulation ... Code Length: 412 Bytes. Times: 00:00:10
PL/SQL过程已成功完成。
代码本身比较简单,只不过为了缩短代码的数量,使用了一些小技巧。当然这个实现肯定还有很多可以优化的地方,这里希望能起到一个抛砖引玉的作用。
导入论坛 引用链接 收藏 分享给好友 推荐到圈子 管理 举报
TAG:
