Tuning and Best Practices Oracle PLSQL

download Tuning and Best Practices Oracle PLSQL

of 13

Transcript of Tuning and Best Practices Oracle PLSQL

  • 7/30/2019 Tuning and Best Practices Oracle PLSQL

    1/13

    Copyright 1999 Steven Feuerstein, PL/Solutions Page 1

    Oracle PL/SQL

    Tuning and OptimizationTechniques

    Quiz Problems and Solutions

  • 7/30/2019 Tuning and Best Practices Oracle PLSQL

    2/13

    Copyright 1999 Steven Feuerstein, PL/Solutions Page 2

    Tuning Data Structures - Before

    File: slowds_q1.sql

    ALTER TABLE employee ADD info VARCHAR2(2000);

    CREATE OR REPLACE PACKAGE comm_pkgIS

    TYPE reward_rt IS RECORD (nm VARCHAR2(2000),sal NUMBER,

    comm NUMBER);

    TYPE reward_tt IS TABLE OF reward_rtINDEX BY BINARY_INTEGER;

    END;/

    CREATE OR REPLACE PROCEDURE fix_me (nmfilter IN VARCHAR2,comm_list IN OUT comm_pkg.reward_tt

    )IS

    v_nmfilter VARCHAR2(2000) NOT NULL := nmfilter;v_info VARCHAR2(2000);v_nth INTEGER;

    indx INTEGER;

    BEGINFOR indx IN comm_list.FIRST .. comm_list.LAST

    LOOPv_nth := v_nth + 1;/*

    || Record date on which increase occurred; time is not

    || important. Job is run at noon and lasts 15 minutes.*/v_info :=

    'Doubled ' || v_nth || 'th employee''s salary on ' ||SYSDATE || ' to ' || comm_list(indx).sal * 2;

    IF UPPER (comm_list(indx).nm) LIKE UPPER (v_nmfilter)THEN

    UPDATE employee

    SET salary := comm_list(indx).sal * 2,info := v_info,

    commission := comm_list(indx).commWHERE last_name = UPPER (comm_list(indx).nm);comm_list(indx).comm := 0;

    END IF;

    END LOOP;END;

    /

  • 7/30/2019 Tuning and Best Practices Oracle PLSQL

    3/13

    Copyright 1999 Steven Feuerstein, PL/Solutions Page 3

    Tuning Data Structures - After

    File: slowds_a1.sql

    /* 1. Move collection out of parameter list and into package spec */

    CREATE OR REPLACE PACKAGE comm_pkgIS

    TYPE reward_rt IS RECORD (nm VARCHAR2(2000),sal NUMBER,

    comm NUMBER);

    TYPE reward_tt IS TABLE OF reward_rtINDEX BY BINARY_INTEGER;

    rewards reward_tt;

    END;

    /

    CREATE OR REPLACE PROCEDURE fix_me (

    nmfilter IN VARCHAR2)

    IS/* 2. Use %TYPE whenever appropriate to reduce memory requirements

    and improve robustness of code. */

    /* 3. UPPER the name filter here and not in the loop. Remove NOT NULL constraint.*/

    v_nmfilter CONSTANT employee.last_name%TYPE := UPPER (nmfilter);v_name employee.last_name%TYPE;

    v_sal employee.salary%TYPE;

    /* 4. Use PLS_INTEGER */

    /* 5. Grab first row in table to do optimal scanning code. */

    indx PLS_INTEGER := comm_pkg.rewards.FIRST;

    /* 7. Date is not going to change, so capture and convert once. */

    v_date CONSTANT VARCHAR2(10) := TO_CHAR (SYSDATE, 'MM/DD/YYYY');

    /* 8. Use PLS_INTEGER */v_first PLS_INTEGER;

    BEGIN

    /* 9. Do explicit NOT NULL check here */

    IF nmfilter IS NOT NULL

    THENv_first := indx - 1;

    LOOP

    EXIT WHEN indx IS NULL;

  • 7/30/2019 Tuning and Best Practices Oracle PLSQL

    4/13

    Copyright 1999 Steven Feuerstein, PL/Solutions Page 4

    /* 10. Move repeated operations to local variables. */

    v_name := UPPER (comm_pkg.rewards(indx).nm);v_sal := comm_pkg.rewards(indx).sal * 2;

    /* 11. Skip assignment to v_info; put in SQL directly. */

    IF v_name LIKE v_nmfilterTHEN

    UPDATE employee

    SET salary = v_sal,info = 'Doubled ' || TO_CHAR (indx - v_first) ||

    'th employee''s salary on ' ||

    v_date || ' to ' || TO_CHAR (v_sal),commission = comm_pkg.rewards(indx).comm

    WHERE last_name = v_name;comm_pkg.rewards(indx).comm := 0;

    END IF;END LOOP;

    END IF;END;/

  • 7/30/2019 Tuning and Best Practices Oracle PLSQL

    5/13

    Copyright 1999 Steven Feuerstein, PL/Solutions Page 5

    Present Value Calculation

    Package Specification and Util ities

    File: presvalue.sql

    CREATE OR REPLACE PACKAGE pv

    I S/ * Tabl e st r uct ur e t o hol d t he l ease accumul at i ons. */TYPE pv_t abl e_t ype I S TABLE OF NUMBER( 9) I NDEX BY BI NARY_I NTEGER;pv_t abl e pv_t abl e_t ype;

    PROCEDURE showt ab;PROCEDURE bui l d_l ease_schedul e1( di sp I N BOOLEAN : = FALSE) ;PROCEDURE bui l d_l ease_schedul e2( di sp I N BOOLEAN : = FALSE) ;PROCEDURE bui l d_l ease_schedul e3( di sp I N BOOLEAN : = FALSE) ;

    END;

    /CREATE OR REPLACE PACKAGE BODY pvI S

    FUNCTI ON pv_of _f i xed (year I N I NTEGER) RETURN NUMBER I SBEGI N

    / * Dummy computat i on */RETURN ( year * 1. 25) ;

    END;

    FUNCTI ON pv_of _var i abl e ( year I N I NTEGER) RETURN NUMBER I SBEGI N

    / * Dummy computat i on */RETURN ( year / 1. 25) ;

    END;

    PROCEDURE showt ab I Si ndx I NTEGER;

    BEGI N/ * Di spl ay t he r esul t s */i ndx : = pv_t abl e. FI RST;LOOP

    EXI T WHEN i ndx I S NULL;DBMS_OUTPUT. PUT_LI NE (

    ' PV i n year ' | | i ndx | | ' = ' | | pv_tabl e( i ndx) ) ;i ndx : = pv_t abl e. NEXT ( i ndx) ;

    END LOOP;END;

  • 7/30/2019 Tuning and Best Practices Oracle PLSQL

    6/13

    Copyright 1999 Steven Feuerstein, PL/Solutions Page 6

    Present Value Calculation - Version 1File: presvalue.sql

    PROCEDURE bui l d_l ease_schedul e1(di sp I N BOOLEAN : = FALSE)/ * Const r uct pr esent val ue l ease schedul e over 20 years. */I S

    / * Temporar y var i abl e t o hol d l ease accumul at i on. */pv_t ot al _l ease NUMBER( 9) ;

    BEGI NFOR year_ count i n 1 . . 20LOOP

    / * Reset t he l ease amount f or t hi s year . */pv_t ot al _l ease : = 0;/ *| | Bui l d t he PV based on t he remai ni ng years

    | | pl us t he f i xed and var i abl e amount s.*/FOR year_count 2 i n year_count . . 20LOOP

    / * Add annual t otal l ease amount t o cummul at i ve. */pv_t ot al _l ease : =

    pv_t ot al _l ease +pv_of _f i xed ( year_count 2) +pv_of _var i abl e ( year _count 2) ;

    END LOOP;

    / * Add t he annual PV t o the tabl e. */pv_t abl e ( year_count ) : = pv_t ot al _l ease;

    END LOOP;

    I F di sp THEN showt ab; END I F;END;

    Hint for Tuning

    Analyze the frequency of execution of the pv_of_* functions.

  • 7/30/2019 Tuning and Best Practices Oracle PLSQL

    7/13

    Copyright 1999 Steven Feuerstein, PL/Solutions Page 7

    Present Value Calculation - Version 2File: presvalue.sql

    PROCEDURE build_lease_schedule2

    (disp IN BOOLEAN := FALSE)IS

    one_year_pv NUMBER(9) := 0;pv_total_lease NUMBER(9) := 0;

    BEGIN/*|| Build the 20-year accumulated total and save each

    || of the annual lease amounts to the PL/SQL table. Notice that

    || pv_table (N) is set to the annual lease amount for year N-1.*/FOR year_count in 1 .. 20

    LOOPone_year_pv :=

    pv_of_fixed (year_count) + pv_of_variable (year_count);

    pv_total_lease := pv_total_lease + one_year_pv;IF year_count < 20THEN

    pv_table (year_count+1) := one_year_pv;END IF;

    END LOOP;

    /* Save the 20-year total in the first row. */

    pv_table (1) := pv_total_lease;

    /* For each of the remaining years... */

    FOR year_count IN 2 .. 20LOOP

    /* Subtract the annual amount from the remaining total. */

    pv_total_lease := pv_total_lease - pv_table (year_count);

    /*|| Save the Nth accumulation to the table (this writes right|| over the annual lease amount, which is no longer needed.

    || I get double use out of the pv_table in this way.*/

    pv_table (year_count) := pv_total_lease;

    END LOOP;

    IF disp THEN showtab; END IF;

    END;

  • 7/30/2019 Tuning and Best Practices Oracle PLSQL

    8/13

    Copyright 1999 Steven Feuerstein, PL/Solutions Page 8

    Present Value Calculation - Version 3File: presvalue.sql

    PROCEDURE bui l d_l ease_schedul e3( di sp I N BOOLEAN : = FALSE)

    I Spv_t ot al _l ease NUMBER( 9) : = 0;one_year _pv NUMBER( 9) : = 0;

    BEGI NFOR year_count I N REVERSE 1. . 20LOOP

    one_year_ pv : = pv_of _f i xed( year_ count ) +pv_of _var i abl e( year _count ) ;

    pv_t otal _l ease : = pv_t otal _l ease + one_year_ pv;pv_t abl e ( year_count ) : = pv_t ot al _l ease;

    END LOOP;

    I F di sp THEN showt ab; END I F;END;

  • 7/30/2019 Tuning and Best Practices Oracle PLSQL

    9/13

    Copyright 1999 Steven Feuerstein, PL/Solutions Page 9

    LStrip Function - Version 1File: lstrip1.sf

    CREATE OR REPLACE FUNCTION lstrip1 (

    string_in IN VARCHAR2,substring_in IN VARCHAR2,

    num_in IN INTEGER := 1)RETURN VARCHAR2

    ISBEGIN

    IF num_in < 1

    OR string_in IS NULL

    OR substring_in IS NULLTHEN

    RETURN string_in;

    ELSIF INSTR (string_in, substring_in) = 1THEN

    RETURN lstrip1 (

    SUBSTR (string_in, LENGTH (substring_in) + 1),substring_in,num_in - 1

    );ELSE

    RETURN string_in;END IF;

    END;

    /

  • 7/30/2019 Tuning and Best Practices Oracle PLSQL

    10/13

    Copyright 1999 Steven Feuerstein, PL/Solutions Page 10

    LStrip Function - Version 2File: lstrip2.sf

    CREATE OR REPLACE FUNCTION lstrip2 (

    string_in IN VARCHAR2,substring_in IN VARCHAR2,

    num_in IN INTEGER := 1)RETURN VARCHAR2

    ISretval VARCHAR2(32767);subnum INTEGER := 0;

    sublen INTEGER := LENGTH (substring_in);

    subpos INTEGER;BEGIN

    retval := string_in;

    WHILE (subnum < num_in)LOOP

    subpos := INSTR (retval, substring_in, 1);

    IF (subpos = 1)THEN

    retval := SUBSTR (retval, sublen + 1);

    subnum := subnum + 1;ELSE

    subnum := num_in;END IF;

    END LOOP;

    RETURN retval;

    END lstrip2;/

    Commentary

    The algorithm in this case uses a WHILE loop. For each iteration of the loop body, it checks to see if the

    sub-string occurs at the beginning of the string. If so, that sub-string is stripped off using SUBSTR and

    tested again. So which is the better implementation (version 1 or version2)? Neither is particularly longor complex. One should, however, always be concerned about the use of recursion, because it can be

    very resource-intensive.

    - Danny Wagenaar ([email protected]):

  • 7/30/2019 Tuning and Best Practices Oracle PLSQL

    11/13

    Copyright 1999 Steven Feuerstein, PL/Solutions Page 11

    LStrip Function - Version 3File: lstrip3.sf

    CREATE OR REPLACE FUNCTION lstrip3 (string_in IN VARCHAR2,

    substring_in IN VARCHAR2,num_in IN INTEGER := 1)

    RETURN VARCHAR2IS

    v_length NUMBER;v_clip VARCHAR2(32767);v_clipcount INTEGER := 0;

    BEGIN

    v_length := LENGTH (substring_in);WHILE v_clipcount < num_inLOOP

    v_clip :=SUBSTR (

    string_in, v_clipcount * v_length + 1, v_length);

    EXIT WHEN v_clip != substring_in;v_clipcount := v_clipcount + 1;

    END LOOP;

    RETURN SUBSTR (string_in, v_clipcount * v_length + 1);END;

    /

    Commentary

    "We avoided the use of the char_in parameter in our implementation. We were careful not to userecursion to make sure there would be no problem with nesting depth. Also, we do not use INSTR to see

    if the substring_in is at the beginning of string_in, since this could potentially lead to a search through32767 bytes for a nonexistent pattern.

    "My new solution to the lstrip problem is significantly faster than the one I sent yesterday when used to

    strip large numbers of substrings from the input string. It is also much faster on stripping small

    iterations, although that is not really noticeable to the user unless the function is called a lot. On top ofthat, the code is smaller while maintaining readability."

    - Ken Geis and Jay Weiland ([email protected] [email protected])

  • 7/30/2019 Tuning and Best Practices Oracle PLSQL

    12/13

    Copyright 1999 Steven Feuerstein, PL/Solutions Page 12

    DBMS_SQL Binding Optimization

    Large Volume of BindingFile: slowsql_q2.sql

    CREATE OR REPLACE PROCEDURE bindall

    IScur INTEGER := DBMS_SQL.open_cursor;

    rows_inserted INTEGER;

    BEGIN

    DBMS_SQL.parse (

    cur,'INSERT INTO emp (empno, deptno, ename)

    VALUES (:empno, :deptno, :ename)',DBMS_SQL.native);

    FOR rowind IN 1 .. 1000LOOP

    DBMS_SQL.bind_variable (cur, 'empno', rowind);

    DBMS_SQL.bind_variable (cur, 'deptno', 40);DBMS_SQL.bind_variable (cur, 'ename', 'Steven' || rowind);

    rows_inserted := DBMS_SQL.execute (cur);

    END LOOP;

    DBMS_SQL.close_cursor (cur);

    END;

    /

  • 7/30/2019 Tuning and Best Practices Oracle PLSQL

    13/13

    Copyright 1999 Steven Feuerstein, PL/Solutions Page 13

    Maybe We Can Avoid Binding Altogether!

    File: slowsql_a2.sql

    CREATE OR REPLACE PROCEDURE bindnoneIS

    cur INTEGER := DBMS_SQL.open_cursor;

    rows_inserted INTEGER;

    BEGINDBMS_SQL.parse (

    cur,'BEGIN

    INSERT INTO emp (empno, deptno, ename)

    VALUES (myvars.empno, myvars.deptno, myvars.ename);

    END;',DBMS_SQL.native

    );

    FOR rowind IN 1 .. 1000

    LOOPmyvars.empno := rowind;myvars.deptno := 40;

    myvars.ename := 'Steven' || rowind;

    rows_inserted := DBMS_SQL.execute (cur);END LOOP;

    DBMS_SQL.close_cursor (cur);END;/

    Run the slowsql_a2.tst to compare the performance.