2014-12-18 21 views
6

Tôi hiện đang cố gắng tối ưu hóa chế độ xem, không phải do tôi viết. Nó thực sự phức tạp, với nhiều lượt xem sử dụng chức năng sử dụng chế độ xem và v.v. Vì vậy, chơi đùa với những gì tôi có thể tối ưu hóa tôi có một cái gì đó tôi có thể không thực sự hiểu: tôi có chức năng này:SUM chậm hơn vòng lặp

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; 

tôi chỉ nghĩ: vòng là xấu, bạn có thể làm tương tự với số tiền:

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; 

Điều lạ lùng là nó thực sự chậm hơn, theo hệ số ~ 2. Không sum chỉ không thích các chức năng? Ai đó có thể giải thích điều này?

Chỉnh sửa: Đây là một câu hỏi lý thuyết. Giải pháp rõ ràng là: tránh sử dụng các chức năng (mà tôi chủ yếu làm, khi tôi tối ưu hóa quan điểm, người khác viết), điều tôi đã làm, nhưng tôi nghĩ, câu hỏi vẫn còn thú vị.

+3

Cách bạn tìm 'tổng hợp' chậm hơn' vòng lặp'. bạn có sử dụng bất kỳ 'DBMS_PROFILER' nào không. đăng kết quả sẽ hữu ích khi tìm giải pháp – Exhausted

+0

Chưa sử dụng DBMS_Profiler. Tôi thực hiện một vòng lặp, mà gọi các chức năng trong bối cảnh tôi cần nó (hơn 8000 lần) mã đầu tiên mất 4-6 giây, thứ hai mất 11-14. –

+0

@ user2179887. . . Hai đoạn mã không tương đương. Đầu tiên là gọi hàm 'verrechnung.get_betrag()' hai lần cho mỗi hàng; thứ hai chỉ gọi nó một lần. Sử dụng cùng một biểu thức trong cả hai, và tôi đoán bạn sẽ thấy rằng 'sum()' là nhanh hơn. –

Trả lời

1

Sự khác biệt về thời gian chạy giảm xuống khi chuyển ngữ cảnh PL/SQL/SQL. Khi nhúng một hàm PL/SQL vào một câu lệnh SQL, Oracle cần phải chuyển đổi qua lại giữa hai công cụ. Hãy xem xét bài kiểm tra rất đơn giản này mà vẫn minh họa sự khác biệt.

Tạo một chức năng đơn giản

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. 

Query đơn giản thực hiện bổ sung và kết hợp trong SQL tinh khiết

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 

Query Simple Performing Aggregation với PL/SQL chức năng gọi

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 

Vì vậy, ví dụ SQL thuần túy mất 0,36 giây trong khi một trong đó có chuyển ngữ cảnh SQL/PLSQL mất nhiều hơn gấp đôi thời gian ở 0,87 giây.

+1

cả hai biến thể của hàm thực hiện chuyển đổi PL/SQL/SQL –

+0

Tôi nghĩ đây là câu trả lời đúng. Tôi không đồng ý với @ a.j.tawleed - chức năng "test" không được gọi nên không có chuyển đổi ngữ cảnh pl/sql trong ví dụ thứ hai. Tôi cũng thấy phong cách của câu trả lời rất tốt. –

+0

Trong câu hỏi, cả hai ví dụ đều có một PL/SQL/SQL-switch –

0

Trong trường hợp SUM, bạn có thể đang đợi bộ nhớ hoặc đĩa. Trình tối ưu hóa có thể đang thực hiện các kết quả của các cuộc gọi hàm của bạn để nó có thể tổng hợp chúng sau khi chúng được tính toán trước. Trong vòng lặp PL/SQL, bạn loại bỏ rõ ràng các hàng đã đọc trước đó, giảm chi phí phân bổ và tối ưu hóa FIRST_ROWS cũng có nghĩa là bạn có thể bắt đầu với toán trước khi đĩa đọc xong tất cả dữ liệu.

Đây là suy đoán, và lược tả là cách duy nhất để biết chắc chắn.

Các vấn đề liên quan