与sum类似的自编mul函数(求乘积)
--十进制
select id,sum(t.value) "和" ,power(10,sum(log(10,t.value)))"乘积"
from
( select 3 id , 4 value from dual
union
select 1 id , 1 value from dual
union
select 1 id , 2 value from dual
union
select 2 id , 3 value from dual
union
select 2 id , 4 value from dual
)t
group by id
--十六进制
select sum(id)"和",power(16,sum(log(16,t.id)))"乘积" from
(
select 1 id from dual
union
select 2 id from dual
union
select 3 id from dual
union
select 17 id from dual
) t;
以上这种写法在当id<=0时,是会报错的,是由于log函数的限制。
select id,sum(t.value) "和" ,power(10,sum(log(10,t.value)))"乘积"
from
( select 3 id , 4 value from dual
union
select 1 id , 1 value from dual
union
select 1 id , 2 value from dual
union
select 2 id , 3 value from dual
union
select 2 id , 0 value from dual
)t
group by id
报错:ORA-01428: 参数'0'超出范围
自定义mul函数,用来求乘积,以游标作为函数的输入参数,编写如下:
--增加了对全部为null情况的判断处理
CREATE OR REPLACE FUNCTION mul(mycursor sys_refcursor) RETURN number
as
m_Multiplynumber number(10);
m_number number;
m_count number;
m_counttotal number;
begin
m_Multiplynumber:=1;
m_count:=0;
m_counttotal:=0;
loop
fetch mycursor into m_number;
EXIT WHEN mycursor%NOTFOUND;
--计算null的个数
if(m_number is null) then m_count:=m_count+1;end if;
--计算总个数
m_counttotal:=m_counttotal+1;
select nvl2(m_number,m_number,1) into m_number from dual;
--if m_number is null then m_number:=1; end if;
m_Multiplynumber:=m_Multiplynumber*m_number;
END LOOP;
CLOSE mycursor;
--null的个数和总个数相等,表示全部为null,则返回的乘积结果也为null
if(m_count=m_counttotal) then m_Multiplynumber:=null; end if;
RETURN m_Multiplynumber;
end mul;
SQL> desc te
Name Type Nullable Default Comments
---- ------ -------- ------- --------
A NUMBER Y
ID NUMBER Y
SQL> select * from te ;
A ID
---------- ----------
4 3
1 1
2 1
3 2
4 2
3
SQL> select id,sum(a),mul(cursor(select a from te t1 where t1.id=t.id)) from te t group by id;
ID SUM(A) MUL(CURSOR(SELECTAFROMTET1WHER
---------- ---------- ------------------------------
1 3 2
2 7 12
3 4 4
遇到一个小错误:nvl2以及nvl是存储过程,不是函数,我之前m_number:=nvl2(m_number,m_number,1)报语法错误 :)
需测试mycursor
1、没有记录
2、只有一个null
3、有多条记录其中有null(null在第一个,null在中间,null在最后)
4、有多条记录其中没有null
5、全为null
测试过程如下:
1、没有记录
SQL> select * from te;
A ID
---------- ----------
SQL> select id,sum(a),mul(cursor(select a from te t1 where t1.id=t.id)) from te t group by id;
ID SUM(A) MUL(CURSOR(SELECTAFROMTET1WHER
---------- ---------- ------------------------------
2、只有一个null
SQL> select * from te;
A ID
---------- ----------
1
2 1
SQL> select id,sum(a),mul(cursor(select a from te t1 where t1.id=t.id)) from te t group by id;
ID SUM(A) MUL(CURSOR(SELECTAFROMTET1WHER
---------- ---------- ------------------------------
1 2 2
3、有多条记录其中有null(null在第一个,null在中间,null在最后)
SQL> select * from te;
A ID
---------- ----------
1
2 1
2 --null在第一个
6 2
2 --null在中间
2 2
2 --null在最后
10 3
8 rows selected
SQL> select id,sum(a),mul(cursor(select a from te t1 where t1.id=t.id)) from te t group by id;
ID SUM(A) MUL(CURSOR(SELECTAFROMTET1WHER
---------- ---------- ------------------------------
1 2 2
2 8 12
3 10 10
4、有多条记录其中没有null
SQL> select * from te;
A ID
---------- ----------
1 2
3 2
4 2
3 2
3 3
5 3
6 rows selected
SQL> select id,sum(a),mul(cursor(select a from te t1 where t1.id=t.id)) from te t group by id;
ID SUM(A) MUL(CURSOR(SELECTAFROMTET1WHER
---------- ---------- ------------------------------
2 11 36
3 8 15
5、全为null
SQL> select * from te;
A ID
---------- ----------
2
2
2
2
2
SQL> select id,sum(a),mul(cursor(select a from te t1 where t1.id=t.id)) from te t group by id;
ID SUM(A) MUL(CURSOR(SELECTAFROMTET1WHER
---------- ---------- ------------------------------
2
另一种写法(朋友的写法)可以借鉴的地方:巧用了m_allNull boolean变量以及不需要做nvl2(m_number,m_number,1)的转换)
CREATE OR REPLACE FUNCTION mul1(mycursor sys_refcursor) RETURN number
as
m_Multiplynumber integer;
m_allNull boolean;
m_number integer;
begin
m_Multiplynumber:=1;
m_allNull:=true;
loop
fetch mycursor into m_number;
dbms_output.put_line(m_number);
EXIT WHEN mycursor%NOTFOUND;
if (m_number is null) then
null;
else
m_Multiplynumber:=m_Multiplynumber*m_number;
m_allNull:=false;
end if;
end loop;
CLOSE mycursor;
if (m_allNull) then
RETURN null;
else
RETURN m_Multiplynumber;
end if;
end mul1;
进一步的问题:
1、如果Cursor中取到的不是整数,甚至不是数字,如何处理
2、如果mul返回溢出,如何处理
现在改写后的存储过程
CREATE OR REPLACE FUNCTION mul(mycursor sys_refcursor) RETURN number
as
m_Multiplynumber number;
m_number number;
m_count number;
m_counttotal number;
begin
m_Multiplynumber:=1;
m_count:=0;
m_counttotal:=0;
loop
fetch mycursor into m_number;
EXIT WHEN mycursor%NOTFOUND;
--计算null的个数
if(m_number is null) then m_count:=m_count+1;end if;
--计算总个数
m_counttotal:=m_counttotal+1;
select nvl2(m_number,m_number,1) into m_number from dual;
--if m_number is null then m_number:=1; end if;
m_Multiplynumber:=m_Multiplynumber*to_number(m_number);
dbms_output.put_line('m_number:'||m_number);
dbms_output.put_line('to_number(m_number):'||to_number(m_number));
END LOOP;
CLOSE mycursor;
--null的个数和总个数相等,表示全部为null,则返回的乘积结果也为null
if(m_count=m_counttotal) then m_Multiplynumber:=null; end if;
RETURN m_Multiplynumber;
end mul;
CREATE OR REPLACE FUNCTION mul1(mycursor sys_refcursor) RETURN number
as
m_Multiplynumber number;
m_allNull boolean;
m_number number;
begin
m_Multiplynumber:=1;
m_allNull:=true;
loop
fetch mycursor into m_number;
dbms_output.put_line(m_number);
EXIT WHEN mycursor%NOTFOUND;
if (m_number is null) then
null;
else
m_Multiplynumber:=m_Multiplynumber*TO_NUMBER(m_number);
m_allNull:=false;
end if;
end loop;
CLOSE mycursor;
if (m_allNull) then
RETURN null;
else
RETURN m_Multiplynumber;
end if;
end mul1;
测试过程如下:
SQL> desc tte
Name Type Nullable Default Comments
---- -------------- -------- ------- --------
ID NUMBER Y
A VARCHAR2(4000) Y
1、对不是数字的情况处理,在m_number前加了to_number函数,自己会报错的;
SQL> select * from tte;
ID A
---------- --------------------------------------------------------------------------------
1 1
1 a
1 2
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999
11 rows selected
SQL> select id,sum(a),mul(cursor(select a from tTe t1 where t1.id=t.id)) from tTe t
2 WHERE T.ID=1
3 group by id
4 ;
select id,sum(a),mul(cursor(select a from tTe t1 where t1.id=t.id)) from tTe t
WHERE T.ID=1
group by id
ORA-01722: 无效数字
2、对于取到的不是整数的情况,我觉得没有关系的,正常算就可以了;
SQL> select * from tte;
ID A
---------- --------------------------------------------------------------------------------
1 1
1 2.4
1 2
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999
SQL> select id,sum(a),mul(cursor(select a from tTe t1 where t1.id=t.id)) from tTe t
2 WHERE T.ID=1
3 group by id ;
ID SUM(A) MUL(CURSOR(SELECTAFROMTTET1WHE
---------- ---------- ------------------------------
1 5.4 4.8
3、如果mul返回溢出,如何处理
SQL> select * from tte;
ID A
---------- --------------------------------------------------------------------------------
1 1
1 a
1 2
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999 --100个9
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999
SQL> select id,sum(a),mul(cursor(select a from tte t1 where t1.id=t.id)) from tte t
2 WHERE T.ID=2
3 group by id ;
ID SUM(A) MUL(CURSOR(SELECTAFROMTTET1WHE
---------- ---------- ------------------------------
2 2E100 1E126
m_Multiplynumber number;刚才错了,原来是写的number(10),所以报ORA-06502错误
我又加了多行2,99999999999999999999999999999999999999999999999999999999999999999999999999999999
SQL> select * from tte;
ID A
---------- --------------------------------------------------------------------------------
1 1
1 2.4
1 2
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999
2 99999999999999999999999999999999999999999999999999999999999999999999999999999999
SQL> select id,sum(a),mul(cursor(select a from tte t1 where t1.id=t.id)) from tte t
2 WHERE T.ID=2
3 group by id;
ID SUM(A) MUL(CURSOR(SELECTAFROMTTET1WHE
---------- ---------- ------------------------------
2 8E100 1E126
发现算出来的乘积不变的,还是1E126,原因是number的最大是1E126
继续执行
begin
for i in 1..10000000 loop
insert into tte(id,a)
values(2,9999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999999);
end loop;
end;
SQL> select id,sum(a),mul(cursor(select a from tte t1 where t1.id=t.id)) from tte t
2 WHERE T.ID=2
3 group by id;
ID SUM(A) MUL(CURSOR(SELECTAFROMTTET1WHE
---------- ---------- ------------------------------
2 8E100 1E126
上面的结果仍然是8E100和1E126
得出的结论是:
oracle自带的sum函数求出来的结果也是有溢出情况存在的,并且溢出的界限值是8E100;
处于自己编写的mul函数和oracle自带的sum函数类似,溢出情况是合理的。
那么自己编写的mul函数由于number类型的限制,并且溢出的界限值是1E126