与sum类似的自编mul函数(求乘积)

与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

 

 

 


 

你可能感兴趣的:(Oracle)