2014-12-18 15 views
6

Obecnie próbuję zoptymalizować widok, który nie został napisany przeze mnie. To jest naprawdę skomplikowane, z wieloma widokami za pomocą funkcji wykorzystującej widoki i tak dalej. Tak, zabawy z co mogę zoptymalizować mam coś naprawdę nie mogę zrozumieć: mam tej funkcji:SUMA jest wolniejsza od pętli

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; 

Pomyślałem: pętle są złe, można zrobić to samo z sumy:

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; 

Dziwne jest to, że w rzeczywistości jest wolniejsze, o współczynnik ~ 2. Czy sum po prostu nie lubię funkcji? Czy ktoś może to wyjaśnić?

Edytuj: To jest bardziej teoretyczne pytanie. Oczywistym rozwiązaniem jest: unikać używania funkcji (co robię głównie, gdy optymalizuję poglądy, napisał ktoś inny), co zrobiłem, ale myślę, że pytanie wciąż jest interesujące.

+3

Jak znaleźć 'sum' jest wolniejsze niż' loop'. używasz dowolnego 'DBMS_PROFILER'. zaksięgowanie wyniku będzie pomocne w znalezieniu rozwiązania – Exhausted

+0

Nie korzystałem jeszcze z DBMS_Profiler. Zrobiłem pętlę, która wywołuje funkcję w kontekście, którego potrzebuję (ponad 8000 razy), pierwszy kod zajmuje 4-6 sekund, drugi zajmuje 11-14. –

+0

@ user2179887. . . Dwa fragmenty kodu nie są równoważne. Pierwszym jest wywołanie funkcji 'verrechnung.get_betrag()' dwukrotnie dla każdego wiersza; drugi nazywa go tylko raz. Użyj tego samego wyrażenia w obu, a domyślam się, że 'sum()' jest szybsze. –

Odpowiedz

1

Różnica w czasie wykonywania zależy od przełączania kontekstu PL/SQL/SQL. Podczas osadzania funkcji PL/SQL w instrukcji SQL Oracle musi przełączać się między tymi dwoma silnikami. Rozważmy to bardzo uproszczone badanie, które jednak pokazuje różnicę.

Tworzenie prostej funkcji

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. 

Proste zapytania wykonując dodawanie i agregację w czystym 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 

Proste Zapytanie Performing Aggregation z PL funkcja/SQL połączenia

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 

Zatem czysty przykład SQL zajmuje 0,36 sekundy, podczas gdy ten z kontekstowym przełącznikiem SQL/PLSQL zajmuje nieco ponad dwa razy tyle czasu, co 0,87 sekundy.

+1

oba warianty funkcji zmieniają PL/SQL/SQL –

+0

Myślę, że to jest poprawna odpowiedź. Nie zgadzam się z @ a.j.tawleed - funkcja "test" nie jest wywoływana, więc nie ma przełącznika kontekstu pl/sql w drugim przykładzie. Uważam też, że styl odpowiedzi jest bardzo dobry. –

+0

W pytaniu oba przykłady mają przełącznik PL/SQL/SQL –

0

W przypadku SUM możesz oczekiwać na pamięć lub dysk. Optymalizator może zmaterializować wyniki swoich wywołań funkcji, aby mógł je podsumować po uprzednim obliczeniu. W pętli PL/SQL wyraźnie odrzucasz wcześniej przeczytane wiersze, redukując koszty alokacji, a optymalizacja FIRST_ROWS może również oznaczać, że możesz zacząć z matematyką przed zakończeniem odczytu wszystkich danych.

To spekulacja, a profilowanie to jedyny sposób, aby się upewnić.

Powiązane problemy