SUM比循环慢

时间:2014-12-18 12:25:21

标签: sql oracle plsql

我目前正在尝试优化视图,这不是我写的。它非常复杂,使用了使用视图等功能的很多视图。 所以,玩弄我可以优化的东西,我有一些我无法理解的东西: 我有这个功能:

create or replace FUNCTION at_get_tourenrechnungssumme_br (in_rechnr IN rechnungen.rechnr%TYPE)
      RETURN NUMBER
   IS
      CURSOR c1 (
         int_rechnr   IN rechnungen.rechnr%TYPE)
      IS
         SELECT (ROUND (
                     verrechnung.get_betrag (bt.buchid, bt.betrag_euro)*(1+b.mwst/100),
                   2))
                   betrag
           FROM buchungen_touren bt, v_buchkz b
          WHERE     bt.rechnr = int_rechnr
                AND bt.storniert_jn = 0
                AND bt.buchid = b.ID;
      int_return   NUMBER (11, 2) := 0;
   BEGIN
      FOR c1_rec IN c1 (in_rechnr)
      LOOP
         int_return := (int_return + c1_rec.betrag);
      END LOOP;

      RETURN NVL (int_return, 0);
   END at_get_tourenrechnungssumme_br;

我只是想:循环很糟糕,你可以用sum来做同样的事情:

create or replace FUNCTION at_get_tourenrechnungssumme_br (in_rechnr IN rechnungen.rechnr%TYPE)
      RETURN NUMBER
   IS
      int_return   NUMBER (11, 2) := 0;
   BEGIN
      SELECT sum(ROUND (
                     verrechnung.get_betrag (bt.buchid, bt.betrag_euro)*(1+b.mwst/100),
                   2))
                   betrag
           into int_return        
           FROM buchungen_touren bt, v_buchkz b
          WHERE     bt.rechnr = in_rechnr
                AND bt.storniert_jn = 0
                AND bt.buchid = b.ID;

      RETURN NVL (int_return, 0);
   END at_get_tourenrechnungssumme_br;

奇怪的是,它实际上很慢,因为〜2。 sum只是不喜欢功能吗? 有人可以解释一下吗?

编辑: 这更像是一个理论问题。显而易见的解决方案是:避免使用函数(当我优化视图,其他人写的时候我会这么做),我做了,但我认为,问题仍然很有趣。

2 个答案:

答案 0 :(得分:1)

运行时间的差异取决于PL / SQL / SQL上下文切换。将PL / SQL函数嵌入到SQL语句中时,Oracle需要在两个引擎之间来回切换。考虑这个非常简单的测试,然而这个测试说明了不同之处。

创建简单功能

SQL> CREATE OR REPLACE FUNCTION test (a IN number, b in NUMBER) RETURN NUMBER
  2  AS
  3  
  4  BEGIN
  5          return a+b;
  6  END;
  7  /

Function created.

简单查询在纯SQL中执行添加和聚合

  1  WITH
  2  row_gen
  3  AS (
  4 SELECT LEVEL as a, level as b
  5 FROM DUAL
  6 CONNECT BY LEVEL < 1000000
  7 )
  8  SELECT SUM(a+b)
  9* FROM row_gen
SQL> /

  SUM(A+B)
----------
1.0000E+12

Elapsed: 00:00:00.36

简单查询使用PL / SQL函数调用执行聚合

  1  WITH
  2  row_gen
  3  AS (
  4 SELECT LEVEL as a, level as b
  5 FROM DUAL
  6 CONNECT BY LEVEL < 1000000
  7 )
  8  SELECT SUM(test(b,b))
  9* FROM row_gen
SQL> /

SUM(TEST(B,B))
--------------
    1.0000E+12

Elapsed: 00:00:00.87

因此,纯SQL示例需要0.36秒,而带有SQL / PLSQL上下文切换的示例需要的时间略多于0.87秒的两倍。

答案 1 :(得分:0)

在SUM情况下,您可能正在等待内存或磁盘。优化器可能正在实现函数调用的结果,因此它可以在预先计算后对它们求和。在PL / SQL循环中,您明确地丢弃先前读取的行,降低了分配成本,FIRST_ROWS优化也可能意味着您可以在磁盘读完所有数据之前开始使用数学。

这是推测,而剖析是确定的唯一方法。