oracle查询优化:跨列求和

时间:2016-05-24 17:34:38

标签: sql oracle plsql

我需要有关优化查询的帮助。

我有一个包含许多帐户类型列和相应值的表

如果 accounttype1 包含帐号ID为“2000”且对应值为 chargeamount1 ,则 accounttype1 包含帐号ID“ 2500“,相应的值在 chargeamount2 等等。

enter image description here

我必须通过汇总相应的chargeamount值来获取所有帐户类型列中指定帐户类型的总和。我试图通过如下所示的函数实现这一目标......

select msisdn,
       fn_get_acc_amount ('2000', accounttype1, chargeamount1,  accounttype1_a, chargeamount1_a, accounttype2, chargeamount2, accounttype2_a,
       chargeamount2_a,accounttype3, chargeamount3,accounttype3_a,chargeamount3_a
      )data_account_4508_usage
      from  data_cdr  partition (p20160521)

功能:

    CREATE OR REPLACE function fn_get_acc_amount(p_account varchar2,
p_accounttype1 varchar2, p_chargeamount1 varchar2,p_accounttype1_a varchar2, p_chargeamount1_a varchar2,
p_accounttype2 varchar2, p_chargeamount2 varchar2,p_accounttype2_a varchar2, p_chargeamount2_a varchar2,
p_accounttype3 varchar2, p_chargeamount3 varchar2,p_accounttype3_a varchar2, p_chargeamount3_a varchar2)
return number as

v_accum number := 0;

begin
  if p_accounttype1  = p_account then
    v_accum := v_accum + to_number(nvl(p_chargeamount1,0));
  end if;

  if p_accounttype1_a = p_account then
    v_accum := v_accum + to_number(nvl(p_chargeamount1_a,0));
  end if;

  if p_accounttype2 = p_account then
    v_accum := v_accum + to_number(nvl(p_chargeamount2,0));
  end if;

  if p_accounttype2_a = p_account then
    v_accum := v_accum + to_number(nvl(p_chargeamount2_a,0));
  end if;

  if p_accounttype3 = p_account then
    v_accum := v_accum + to_number(nvl(p_chargeamount3,0));
  end if;

  if p_accounttype3_a = p_account then
    v_accum := v_accum + to_number(nvl(p_chargeamount3_a,0));
  end if;

  return nvl(v_accum,0);

end;
/

在我的IDE上运行并返回一个值...但是必须创建或将输出插入到另一个表中,因为表data_cdr有大约3000万条记录。

以下是我尝试优化功能:

CREATE OR REPLACE function fn_get_acc_amount(
p_account varchar2,
p_accounttype1 varchar2, p_chargeamount1 varchar2,p_accounttype1_a varchar2, p_chargeamount1_a varchar2,
p_accounttype2 varchar2, p_chargeamount2 varchar2,p_accounttype2_a varchar2, p_chargeamount2_a varchar2,
p_accounttype3 varchar2, p_chargeamount3 varchar2,p_accounttype3_a varchar2, p_chargeamount3_a varchar2)
return number 
result_cache
is
v_accum number;
v_sql varchar2 (4000);
begin
 v_accum :=0;
 v_sql:='with acc_table as 
        (
            select :1 accounttype, nvl(:2,0) chargeamount from dual union all
            select :3, nvl (:4,0 ) from dual union all 
            select :5, nvl (:6,0 ) from dual union all 
            select :7, nvl (:8,0 ) from dual union all 
            select :9, nvl (:10,0 ) from dual union all 
            select :11, nvl (:12,0 ) from dual                 
        )
  select sum(chargeamount) from acc_table
  where accounttype ='||p_account;

  execute immediate v_sql into v_accum
  using p_accounttype1 , p_chargeamount1 ,p_accounttype1_a , p_chargeamount1_a ,
        p_accounttype2 , p_chargeamount2 ,p_accounttype2_a , p_chargeamount2_a ,
        p_accounttype3 , p_chargeamount3 ,p_accounttype3_a , p_chargeamount3_a ;

  return nvl(v_accum,0);

end;
/

它仍在继续运行。 我需要建议,我可以优化这一点。我试过寻找枢轴查询,但我无法让它工作。

我将非常感激我能获得的任何见解。

2 个答案:

答案 0 :(得分:1)

直接的SQL语句几乎可以肯定比在函数中执行的任何操作(更快)运行 - 特别是如果您一次读取一行。

这样的事情可能有用:

select [whatever other columns,]
         case p_accounttype1   when '2000' then p_chargeamount1   else 0 end +
         case p_accounttype1_a when '2000' then p_chargeamount1_a else 0 end +
         case p_accounttype2   when '2000' then p_chargeamount2   else 0 end +
         case p_accounttype2_a when '2000' then p_chargeamount2_a else 0 end +
         case p_accounttype3   when '2000' then p_chargeamount3   else 0 end +
         case p_accounttype3_a when '2000' then p_chargeamount3_a else 0 end
       as data_account_4508_usage
from [......]

或者,如果你需要所有列中这些总和的总和(我无法从叙述中看出来并且我没有阅读你的函数,抱歉) - 你可以将整个案例表达式的总和包装在一个大的外部SUM()。

如果您需要针对不同的帐户ID运行此变量,可以为'2000'插入绑定变量。

话虽如此,如果您的组织中有任何权力,您应该强烈主张规范化您的数据模型。你不应该为了最简单的查询之一而跳过这样的箍。祝你好运!

答案 1 :(得分:0)

我的第一个想法是改变你的查询:

select msisdn, sum(amount) from (
select msisdn, chargeamount1 as amount from data_cdr partition (p20160521) where accounttype1 = '2000' and chargeamount1 is not null
union all
select msisdn, chargeamount1_a from data_cdr partition (p20160521) where accounttype1_a = '2000' and chargeamount1_a is not null
union all
select msisdn, chargeamount2 from data_cdr partition (p20160521) where accounttype2 = '2000' and chargeamount2 is not null
union all
select msisdn, chargeamount2_a from data_cdr partition (p20160521) where accounttype2_a = '2000' and chargeamount2_a is not null
union all
select msisdn, chargeamount3 from data_cdr partition (p20160521) where accounttype3 = '2000' and chargeamount3 is not null
union all
select msisdn, chargeamount3_a from data_cdr partition (p20160521) where accounttype3_a = '2000' and chargeamount3_a is not null
) as tmp
group by msisdn

当然,您的金额列是整数(不是字符串),并且您在每个帐户类型列上都有不同的索引以及所有需要的字段