最小源代码擂台[ITPub]
				
		
		
				
				 
		
				
						
								    很久以前看过的一个帖子,今天又翻到了,觉得还挺有意思的,摘录一下:
				
		
		
		
				
						---------------------------------------------
				
		
		
				
				 
		
				题目: 请模拟出 Oracle 中 Add_Months 函数功能的一个自定义函数.
要求:
1. 函数申明固定为:
create or replace function my_add_months(p_date_string varchar2,
                                         p_months      number)
  return varchar2
  不允许更改.
		
		 
		
				2. 代码中禁止申明日期类型的变量, 禁止使用一切与日期有关的类型转换及函数,禁止使用Oracle提供的Package, 只能使用 Oracle 的标准函数
		
		
				
						
3. 不求代码的效率高效, 但也不能太低.
		
		 
		4. 使用你可以使用的一切手段, 将源代码缩短, 不考虑编译后的代码长度, 这里只要求源代码最短.
		 
		5. 代码的长度以扣除其中的空白(空格, 回车换行, TAB键)后的字节长度.
		 
		6. 传入的日期字符串格式为 yyyymmdd, 增加的月份数传入整数,  函数内部你不需要检核传入的日期字符串是否有效(一定传入有效的), 月份数也不用考虑带小数的情况, 返回的日期字符串格式依然为 yyyymmdd
		 
		7. 写完函数后, 请用以下代码进行测试, 希望运行时间不要太长哦.
		 
		8. 只把测试结果贴出来, 函数代码先不贴.
		 
		
		 
		10. 函数代码中禁止使用SQL语句.
		 
		 
		
				-----------------------
		
		
				
				 
		
				
				 
		
				测试脚本:
		
		
				
				 
		
				
						
								
										set
								
								 serverout 
								
										on
								
								
										
										
										
								
						
						
								
										 
								
						
						
								
										declare
								
								
										
										
								
						
						
								
										  ln  
								
										number
								
								;
						
						
								
										  ld  
								
										date
								
								;
						
						
								
										  ls1 
								
										varchar2
								
								(
								8
								);
						
						
								
										  ls2 
								
										varchar2
								
								(
								8
								);
						
						
								
										  lt  
								
										number
								
								 := dbms_utility.get_time;
						
						
								
										  ex 
								
										exception
								
								;
						
						
								
										  y 
								
										number
								
								;
						
						
								
										  m 
								
										number
								
								;
						
						
								
										  d 
								
										number
								
								;
						
						
								
										  j 
								
										number
								
								;
						
						
								
										begin
								
								
										
										
								
						
						
								
										  
								
								
										for
								
								 j 
								
										in
								
								
								
								0
								 .. 
								5000
								
								
								
										loop
								
								
										
										
								
						
						
								
										    
								
								
										for
								
								 y 
								
										in
								
								
								
								2000
								 .. 
								2001
								
								
								
										loop
								
								
										
										
								
						
						
								
										      
								
								
										for
								
								 m 
								
										in
								
								
								
								2
								 .. 
								4
								
								
								
										loop
								
								
										
										
								
						
						
								
										        
								
								
										for
								
								 d 
								
										in
								
								
								
								28
								 .. 
								31
								
								
								
										loop
								
								
										
										
								
						
						
								
										          
								
								
										begin
								
								
										
										
								
						
						
								
										            ls1 := y || 
								'0'
								 || m || d;
						
						
								
										            
								
								
										begin
								
								
										
										
								
						
						
								
										              ld  := to_date(ls1, 
								'yyyymmdd'
								);
						
						
								
										            
								
								
										exception
								
								
										
										
								
						
						
								
										              
								
								
										when
								
								
								
								
										others
								
								
								
								
										then
								
								
										
										
								
						
						
								
										                
								
								
										exit
								
								; 
								
										--
								
								
										过滤所有格式外的日期
								
								
										
										
								
						
						
								
										            
								
								
										end
								
								;
						
						
								
										            
								
								
										--
								
								
										正的月份计算验证
								
								
										
										
								
						
						
								
										            ld  := add_months(ld, j);
						
						
								
										            ls2 := to_char(ld, 
								'yyyymmdd'
								);
						
						
								
										            
								
								
										if
								
								 nvl(my_add_months(ls1, j), 
								'*'
								) <> ls2 
								
										then
								
								
										
										
								
						
						
								
										              dbms_output.put_line(
								'Sorry: stop at p_date_string='
								 || ls1 ||
						
						
								
										                                   
								
								',p_months='
								 || j);
						
						
								
										              dbms_output.put_line(
								'my_add_months returned: '
								 ||
						
						
								
										                                   my_add_months(ls1, j));
						
						
								
										              dbms_output.put_line(
								'add_months returned: '
								 || ls2);
						
						
								
										              
								
								
										raise
								
								 ex;
						
						
								
										            
								
								
										end
								
								
								
								
										if
								
								;
						
						
								
										            
								
								
										--
								
								
										负的月份计算验证
								
								
										
										
								
						
						
								
										            ls1 := to_char(add_months(ld, -j), 
								'yyyymmdd'
								);
						
						
								
										            
								
								
										if
								
								 nvl(my_add_months(ls2, -j), 
								'*'
								) <> ls1 
								
										then
								
								
										
										
								
						
						
								
										 
										             dbms_output.put_line(
								'Sorry: stop at p_date_string='
								 || ls2 ||
						
						
								
										                                   
								
								',p_months='
								 || -j);
						
						
								
										              dbms_output.put_line(
								'my_add_months returned: '
								 ||
						
						
								
										                                   my_add_months(ls2, -j));
						
						
								
										              dbms_output.put_line(
								'add_months returned: '
								 || ls1);
						
						
								
										              
								
								
										raise
								
								 ex;
						
						
								
										            
								
								
										end
								
								
								
								
										if
								
								;
						
						
								
										          
								
								
										exception
								
								
										
										
								
						
						
								
										            
								
								
										when
								
								 ex 
								
										then
								
								
										
										
								
						
						
								
										              
								
								
										raise
								
								;
						
						
								
										            
								
								
										when
								
								
								
								
										others
								
								
								
								
										then
								
								
										
										
								
						
						
								
										              
								
								
										raise
								
								;
						
						
								
										          
								
								
										end
								
								;
						
						
								
										        
								
								
										end
								
								
								
								
										loop
								
								;
						
						
								
										      
								
								
										end
								
								
								
								
										loop
								
								;
						
						
								
										    
								
								
										end
								
								
								
								
										loop
								
								;
						
						
								
										  
								
								
										end
								
								
								
								
										loop
								
								;
						
						
								
										  
								
								
										--
								
								
										计算函数的字符个数
								
								
										
										
								
						
						
								
										  ln := 
								0
								;
						
						
								
										  
								
								
										for
								
								 c 
								
										in
								
								 (
								
										select
								
								 text
						
						
								
										              
								
								
										from
								
								 user_source
						
						
								
										             
								
								
										where
								
								
								
								
										name
								
								 = 
								'MY_ADD_MONTHS'
								
										
										
								
						
						
								
										               
								
								
										and
								
								
								
								
										type
								
								 = 
								'FUNCTION'
								) 
								
										loop
								
								
										
										
								
						
						
								
										    ln := ln + nvl(lengthb(translate(c.text,
						
						
								
										                                     
								
								'*'
								 || chr(
								9
								) || chr(
								10
								) || chr(
								13
								) || chr(
								32
								),
						
						
								
										                                            
								
								
										--
								
								
										除去所有的空格、回车、换行、
								
								
										tab
								
								
										
										
								
						
						
								
										                                     
								
								'*'
								)),
						
						
								
										                   
								
								0
								);
						
						
								
										  
								
								
										end
								
								
								
								
										loop
								
								;
						
						
								
										  lt := (dbms_utility.get_time - lt) / 
								100
								;
						
						
								
										  dbms_output.put_line(
								'Congratulation ... Code Length: '
								 || ln ||
						
						
								
										                       
								
								' Bytes. Times: '
								 ||
						
						
								
										                       to_char(to_date(to_char(lt, 
								'fm00000'
								), 
								'sssss'
								),
						
						
								
										                               
								
								'hh24:mi:ss'
								));
						
						
								
										exception
								
								
										
										
								
						
						
								
										  
								
								
										when
								
								 ex 
								
										then
								
								
										
										
								
						
						
								
										    
								
								
										null
								
								;
						
						
								
										end
								
								;
						
						
								/
						
						
								
										
												 
										
								
						
				
				
						
				
		 
		 
		
				-----------------------
		
		
				
				 
		
				
				 
		
				随便挑了一个答案来看看,确实是要耗费很多精力的事情:
		
		
				
				 
		
				
						
								--sdxiong
						
				
		
		
				
						
								
										CREATE
								
								
								
								
										OR
								
								
								
								
										REPLACE
								
								
								
								
										FUNCTION
								
								 MY_ADD_MONTHS(P_DATE_STRING 
								
										VARCHAR2
								
								,
						
						
								
										                                         P_MONTHS      
								
										NUMBER
								
								)
						
						
								
										  
								
								
										RETURN
								
								
								
								
										VARCHAR2
								
								
								
								
										IS
								
								
										
										
								
						
						
								
										  H 
								
										INT
								
								 := 
								100
								;
						
						
								
										  
										
										S INT := P_DATE_STRING;
						
						
								
										  Y 
								
										INT
								
								 := S/H/H;
						
						
								
										  M 
								
										INT
								
								 := S/H 
								
										MOD
								
								 H;
						
						
								
										  D 
								
										INT
								
								 := S 
								
										MOD
								
								 H;
						
						
								
										 
								
						
						
								
										  
								
								
										PROCEDURE
								
								 P 
								
										IS
								
								
										
										
								
						
						
								
										  
								
								
										BEGIN
								
								
										
										
								
						
						
								
										    S := 
								28
								 + 
								3232332323030
								 / 
								10
								**M 
								
										MOD
								
								
								
								10
								; 
								
										--
								
								
										每个月最后一天
								
								
										
										
								
						
						
								
										    
								
								
										IF
								
								 M=
								2
								
								
								
										AND
								
								 (Y 
								
										MOD
								
								
								
								400
								 = 
								0
								
								
								
										OR
								
								 Y 
								
										MOD
								
								
								
								4
								 < Y 
								
										MOD
								
								 H / H) 
								
										THEN
								
								
								
								
										--
								
								
										判断
								
								
										Y
								
								
										是否闰年
								
								
										
										
								
						
						
								
										       S := 
								29
								;
						
						
								
										    
								
								
										END
								
								
								
								
										IF
								
								;
						
						
								
										  
								
								
										END
								
								;
						
						
								
										 
								
						
						
								
										BEGIN
								
								
										
										
								
						
						
								
										 
								
						
						
								
										  P;
						
						
								
										  D := D + D * INSTR(D,S); 
								
										--
								
								
										若
								
								
										D
								
								
										为月末,则不需要这个
								
								
										D
								
								
										,取新月份的月末期
								
								
										
										
								
						
						
								
										 
								
						
						
								
										  M := Y*
								12
								+M+P_MONTHS;
						
						
								
										  Y := M/
								12
								-.55; 
								
										--
								
								
										计算新年份
								
								
										(
								
								
										保证不进位或退位
								
								
										)
								
								
										,用
								
								
										 Y:=(M-7)/12
								
								
										好理解一些
								
								
										
										
								
						
						
								
										  M := M-Y*
								12
								; 
								
										--
								
								
										计算新月份
								
								
										
										
								
						
						
								
										 
								
						
						
								
										  P; 
								
										--
								
								
										计算新月份的月末
								
								
										
										
								
						
						
								
										 
								
						
						
								
										  
								
								
										RETURN
								
								 Y*H*H + M*H + LEAST(D,S);
						
						
								
										 
								
						
						
								
										END
								
								;
						
						
								/
						
				
		 
		 
		 
		
				-----------------------
		
		
				
				 
		
				
				 
		
				其它脚本:
		
		
				
				 
		
				
						
/**********************
nyfor:
**********************/
				
						
create or replace function my_add_months
(
   p_date_string varchar2,
   p_months      number
) return varchar2 is
   c int := 100;
   a int := p_date_string;
   y int := a / c / c;
   m int := a / c - y * c;
   t int;
   procedure p is
   begin
      t := substr(525454554545, m, 1);
      t := t + 26 + 1 / (t + mod(y, 4) + instr(0, mod(y, c)) * mod(y, 400));
   end;
begin
   p;
   a := mod(a, c);
   a := trunc(a / t) * c + a;
   m := m + p_months + y * 12 - 7;
   y := m / 12;
   m := m - y * 12 + 7;
   p;
   return y * c * c + m * c + least(a, t);
end;
				
						
--如果要确保取出每一个隐式转换,包含精度的转换, 也会增加很多字节的. 我的转换后如下:
create or replace function my_add_months
(
   p_date_string varchar2,
   p_months      number
) return varchar2 is
   c int := 100;
   a int := to_number(p_date_string);
   y int := round(to_number(a) / c / c);
   m int := round(to_number(a) / c - y * c);
   t int;
   procedure p is
   begin
      t := to_number(substr('525454554545', m, 1));
      t := round(t + 26 + 1 / (t + mod(y, 4) + instr(0, mod(y, c)) * mod(y, 400)));
   end;
begin
   p;
   a := mod(to_number(a), c);
   a := trunc(to_number(a) / t) * c + to_number(a);
   m := m + p_months + y * 12 - 7;
   y := round(m / 12);
   m := m - y * 12 + 7;
   p;
   return to_char(y * c * c + m * c + least(to_number(a), t));
end;
--加入所有的显示转换及精度转换后 475 Bytes.
				 
				 
				 
				 
				 
				/**********************
DragonBill:
***********************/
				 
				--Congratulation ... Code Length: 344 Bytes. Times: 00:00:08
create or replace function my_add_months(p_date_string varchar2,
                                         p_months      number)
  return varchar2
AS
   C INT := p_date_string;
   H INT := 100;
   Y INT := C/H/H;
   M INT := MOD(C/H, H);
   D INT := MOD(C, H);
   Z INT := Y * 12 + M + p_months;
				 
				   PROCEDURE P
   AS
   BEGIN
      C := 27 + SUBSTR(43434434342 - SIGN(MOD(Y, 16 - 4 * INSTR(Y/H,'.'))), 1 - M, 1);
   END;
				 
				BEGIN
				 
				   P;
				 
				   M := MOD(Z - 1, 12) + 1;
   Y := (Z - M) / 12;
   Z := C;
				 
				   P;
				 
				   RETURN Y*H*H + M*H + LEAST(D + TRUNC(D/Z) * 3, C);
END;
				 
				 
				--Congratulation ... Code Length: 339 Bytes. Times: 00:00:08
create or replace function my_add_months(p_date_string varchar2,
                                         p_months      number)
  return varchar2
AS
   C INT := p_date_string;
   H INT := 100;
   Y INT := C/H/H;
   M INT := MOD(C/H, H);
   D INT := MOD(C, H);
   Z INT := Y * 12 + M + p_months;
				 
				   PROCEDURE P
   AS
   BEGIN
      C := 27 + SUBSTR(43434434342 - SIGN(MOD(Y, 16 - 4 * INSTR(Y/H,'.'))), 1 - M, 1);
   END;
				 
				BEGIN
				 
				   P;
				 
				   M := MOD(Z - 1, 12) + 1;
   Y := (Z - M) / 12;
   Z := D/C/2;
				 
				   P;
				 
				   RETURN Y*H*H + M*H + LEAST(D + Z * 3, C);
END;
		
		
				
				 
		
				
				 
		
				/*********************
判断闰年: 非百年看后两位能否被4整除, 百年看前两位能否被4整除,  再往前推, 百年能被100整除, 
又 100 = 4 * 25, 故闰百年必能被16整除, 年/100, 能整除的无小数点, 是百年, 不能整除的有小数点, 
凑巧小数点的位置为3, 综合以上, 故, 非百年 MOD 4 , 百年 MOD 16, 能整除的都是闰年. 
这也就是为什么用 INSTR(Y/H,'.') 而不用 SIGN(MOD(Y, H)) 的原因, 虽然它们的长度是一样, 
但 12 * {0, 1} 和 4 * {0, 3} 相比, 多了一个Byte
		
		 
		
				现在可以飚到329了
**********************/
		
		
				
				 
		--Congratulation ... Code Length: 329 Bytes. Times: 00:00:08
create or replace function my_add_months(p_date_string varchar2,
                                         p_months      number)
  return varchar2
AS
   C INT := p_date_string;
   H INT := 100;
   Y INT := C/H/H;
   M INT := C/H - Y * H;
   D INT := MOD(C, H);
   Z INT := Y * 12 + M + p_months;
		 
		   PROCEDURE P
   AS
   BEGIN
      C := 27 + SUBSTR(43434434342 - SIGN(MOD(Y, 16 - 4 * INSTR(Y/H,'.'))), 1 - M, 1);
   END;
		 
		BEGIN
		 
		   P;
		 
		   Y := (Z - 7) / 12;
   M := Z - Y * 12;
   Z := D/C/2;
		 
		   P;
		 
		   RETURN Y*H*H + M*H + LEAST(D + Z * 3, C);
END;
/
		
				
--Congratulation ... Code Length: 319 Bytes. Times: 00:00:09
create or replace function my_add_months(p_date_string varchar2,
                                         p_months      number)
  return varchar2
AS
   C INT := p_date_string;
   H INT := 100;
   Y INT := C/H/H;
   M INT := C/H - Y * H;
   D INT := C MOD H;
   Z INT := Y * 12 + M + p_months;
		 
		   PROCEDURE P
   AS
   BEGIN
      C := 27 + SUBSTR(43434434341 + 0 ** (Y MOD (4 + 12 * 0 ** (Y MOD H))), 1 - M, 1);
   END;
		 
		BEGIN
		 
		   P;
		 
		   Y := (Z - 7) / 12;
   M := Z - Y * 12;
   Z := D/C/2;
		 
		   P;
		 
		   RETURN Y * H * H + M * H + LEAST(C, Z || D);
END;
/