1 good oracle sql query tuning hints

Upload: luckysantosh

Post on 06-Apr-2018

255 views

Category:

Documents


1 download

TRANSCRIPT

  • 8/2/2019 1 Good Oracle SQL Query Tuning Hints

    1/13

    Oracle SQL Query Tuning Hints

    WHERE Clause

    Try to avoid

    Given Query Alternative

    SELECT ename, hiredate, salFROM empWHERE SUBSTR(ename,1,3) = 'SCO';

    SELECT ename, hiredate, salFROM empWHERE ename LIKE 'SCO%';

    VARIABLE name VARCHAR2(20)exec name := 'SCOTT'

    SELECT ename, hiredate, salFROM empWHERE ename = NVL (:name, ename);

    VARIABLE name VARCHAR2(20)exec name := 'SCOTT'

    SELECT ename, hiredate, salFROM empWHERE ename LIKE NVL (:name, '%');

    SELECT ename, hiredate, salFROM empWHERE TRUNC (hiredate) = TRUNC(SYSDATE);

    SELECT ename, hiredate, salFROM empWHERE hiredate BETWEEN TRUNC (SYSDATE)AND TRUNC (SYSDATE) + .99999;

    SELECT ename, hiredate, salFROM empWHERE ename || empno = 'SCOTT7788';

    SELECT ename, hiredate, salFROM empWHERE ename = 'SCOTT

    AND empno = 7788;

    SELECT ename, hiredate, salFROM empWHERE sal + 3000 < 5000;

    SELECT ename, hiredate, salFROM empWHERE sal < 2000;

    SELECT ename, hiredate, salFROM empWHERE sal != 0;

    SELECT ename, hiredate, salFROM empWHERE sal > 0;

    operations on database objects referenced in the WHERE clause.

    HAVING Clause

    The HAVING clause filters selected rows only after all rows have been fetched. Using a WHERE clause helps

    reduce overheads in sorting, summing, etc. HAVING clauses should only be used when columns withsummary operations applied to them are restricted by the clause.

    Given Query Alternative

    SELECT d.dname, AVG (e.sal)FROM emp e, dept dWHERE e.deptno = d.deptnoGROUP BY d.dnameHAVING dname != 'RESEAECH'AND dname != 'SALES';

    SELECT d.dname, AVG (e.sal)FROM emp e, dept d

    WHERE e.deptno = d.deptnoAND dname != 'RESEAECH'AND dname != 'SALES'GROUP BY d.dname;

  • 8/2/2019 1 Good Oracle SQL Query Tuning Hints

    2/13

    Combined Subqueries

    Minimize the number of table lookups (subquery blocks) in queries, particularly if your statements include

    subquery SELECTs or multicolumn UPDATEs.

    Separate Subqueries Combined Subqueries

    SELECT enameFROM empWHERE sal = (SELECT MAX (sal)

    FROM lookup)AND comm = (SELECT MAX (comm)

    FROM lookup);

    SELECT enameFROM empWHERE (sal,comm) = (SELECT MAX (sal),

    MAX(comm)FROM lookup);

    EXISTS, NOT IN, Table Joins

    Consider the alternatives EXISTS, IN and table joins when doing multiple table joins. None of these

    are consistently faster; it depends on your data.

    SELECT enameFROM emp EWHERE EXISTS (SELECT 'X'

    FROM deptWHERE deptno = E.deptnoAND dname = 'ACCOUNTING');

    SELECT enameFROM emp EWHERE deptno IN (SELECT deptno

    FROM deptWHERE deptno = E.deptnoAND dname = 'ACCOUNTING');

    SELECT enameFROM dept D, emp EWHERE E.deptno = D.deptnoAND D.dname = 'ACCOUNTING';

    DISTINCT

    Avoid joins that require the DISTINCT qualifier on the SELECT list in queries which are used todetermine information at the owner end of a one-to-many relationship. The DISTINCT operator

    causes Oracle to fetch all rows satisfying the table join and then sort and filter out duplicate values.EXISTS is a faster alternative, because the Oracle optimizer realizes when the subquery has been

    satisfied once, there is no need to proceed further and the next matching row can be fetched.

    Given Query Alternative

    SELECT DISTINCT d.deptno, d.dnameFROM dept D,

    emp EWHERE D.deptno = E.deptno;

    SELECT d.deptno, d.dnameFROM dept DWHERE EXISTS (SELECT 'X'

    FROM emp EWHERE E.deptno = D.deptno);

  • 8/2/2019 1 Good Oracle SQL Query Tuning Hints

    3/13

    UNION ALL

    Consider whether a UNION ALL will suffice in place of a UNION. The UNION clause forces all rows

    returned by each portion of the UNION to be sorted and merged and duplicates to be filtered beforethe first row is returned. A UNION ALL simply returns all rows including duplicates and does not have

    to perform any sort, merge or filter. If your tables are mutually exclusive (include no duplicate

    records), or you don't care if duplicates are returned, the UNION ALL is much more efficient.

    UNION UNION ALL

    SELECT acct, balanceFROM debitWHERE trandate = '31-DEC-95'UNIONSELECT acct, balanceFROM creditWHERE trandate = '31-DEC-95';

    SELECT acct, balanceFROM debitWHERE trandate = '31-DEC-95'UNION ALLSELECT acct, balanceFROM creditWHERE trandate = '31-DEC-95';

    DECODE

    Consider using DECODE to avoid having to scan the same rows repetitively or join the same table

    repetitively. Note, DECODE is not necessarily faster as it depends on your data and the complexity ofthe resulting query. Also, using DECODE requires you to change your code when new values are

    allowed in the field.

    SELECT COUNT(*)FROM empWHERE status = 'Y'AND ename LIKE 'SMITH%';----------SELECT COUNT(*)

    FROM empWHERE status = 'N'AND ename LIKE 'SMITH%';

    SELECT COUNT(DECODE(status, 'Y', 'X', NULL)) Y_count,COUNT(DECODE(status, 'N', 'X', NULL)) N_count

    FROM empWHERE ename LIKE 'SMITH%';

    Anti Joins

    An anti-join is used to return rows from a table that that are present in another table. It might be used for

    example between DEPT and EMP to return only those rows in DEPT that didn't join to anything in EMP;

    SELECT *FROM dept

    WHERE deptnoNOT IN (SELECT deptno FROM EMP);

    SELECT dept.*FROM dept, emp

    WHERE dept.deptno = emp.deptno (+)AND emp.ROWID IS NULL;

  • 8/2/2019 1 Good Oracle SQL Query Tuning Hints

    4/13

    SELECT *FROM dept

    WHERE NOT EXISTS (SELECT NULL FROM emp WHERE emp.deptno = dept.deptno);

    Full Outer Joins

    Normally, an outer join of table A to table B would return every record in table A, and if it had a mate intable B, that would be returned as well. Every row in table A would be output, but some rows of table B

    might not appear in the result set. A full outer join would return ebery row in table A, as well as every row intable B. The syntax for a full outer join is new in Oracle 9i, but it is a syntactic convenience, it is possible to

    produce full outer joins sets using conventional SQL.

    update emp set deptno = 9 where deptno = 10;commit;

    Conventional SQL New Syntax

    SELECT empno, ename, dept.deptno, dnameFROM emp, dept

    WHERE emp.deptno(+) = dept.deptnoUNION ALLSELECT empno, ename, emp.deptno, NULLFROM emp, dept

    WHERE emp.deptno = dept.deptno(+)AND dept.deptno IS NULL

    ORDER BY 1,2,3,4;

    EMPNO ENAME DEPTNO DNAME---------- ---------- ------------------- 7369 SMITH 20RESEARCH

    7499 ALLEN 30 SALES

    7521 WARD 30 SALES7566 JONES 20 RESEARCH7654 MARTIN 30 SALES7698 BLAKE 30 SALES7782 CLARK 97788 SCOTT 20 RESEARCH7839 KING 97844 TURNER 30 SALES7876 ADAMS 20 RESEARCH7900 JAMES 30 SALES7902 FORD 20 RESEARCH7934 MILLER 9

    10 ACCOUNTING

    40 OPERATIONS

    SELECT empno, ename,NVL(dept.deptno,emp.deptno) deptno,

    dnameFROM emp FULL OUTER JOIN dept ON

    (emp.deptno = dept.deptno)ORDER BY 1,2,3,4;

    EMPNO ENAME DEPTNO DNAME---------- ---------- ---------- ------------

    7369 SMITH 20 RESEARCH7499 ALLEN 30 SALES

    7521 WARD 30 SALES7566 JONES 20 RESEARCH7654 MARTIN 30 SALES7698 BLAKE 30 SALES7782 CLARK 97788 SCOTT 20 RESEARCH7839 KING 97844 TURNER 30 SALES7876 ADAMS 20 RESEARCH7900 JAMES 30 SALES7902 FORD 20 RESEARCH7934 MILLER 9

    10 ACCOUNTING40 OPERATIONS

    Inline VIEWS

    The inline view is a construct in Oracle SQL where you can place a query in the SQL FROM, clause, just as if

    the query was a table name.

    OK, so why use the complicated select in the first place? Why not just create the view? Well, one good

    reason is that creating a view gives you another database object to maintain, and adds more complexity to

  • 8/2/2019 1 Good Oracle SQL Query Tuning Hints

    5/13

    your system. By placing the view "inside" your main select, you have all of the code needed to support thequery in one place.

    The Power of Inline Views

    Overview

    The inline view is a construct in Oracle SQL where you can place a query in the SQL FROM,

    clause, just as if the query was a table name.

    OK, so why use the complicated select in the first place? Why not just create the view?

    Well, one good reason is that creating a view gives you another database object tomaintain, and adds more complexity to your system. By placing the view "inside" your ma

    select, you have all of the code needed to support the query in one place.

    If you have a query as the following ...

    SELECT aFROM tableWHERE id = :idAND b = (SELECT MAX (b)

    FROM tableWHERE id = :id)

    ... it can be worth to check if an inline view, instead of the subquery will be faster.

    Example 1 (Replace Subquery for MAX)

    With Subquery

    CREATE TABLE test (id INT, height INT, acc_date DATE);

    INSERT INTO test (id, height, acc_date)SELECT MOD(ROWNUM,1000), DBMS_RANDOM.RANDOM,SYSDATE-1000+DBMS_RANDOM.VALUE(0,1000)FROM all_objects;

    6357 rows created.

    COMMIT;

    CREATE INDEX test_idx on test (id, acc_date, height);

    Index created.

    ANALYZE TABLE test COMPUTE STATISTICS

    http://history.back%28%29/
  • 8/2/2019 1 Good Oracle SQL Query Tuning Hints

    6/13

    FOR TABLEFOR ALL INDEXESFOR ALL INDEXED COLUMNS;

    Table analyzed.

    alter session set timed_statistics=true;alter session set sql_trace=true;

    VARIABLE b1 NUMBERexec :b1 := 10

    ALTER SESSION SET TIMED_STATISTICS=TRUE;ALTER SESSION SET SQL_TRACE=TRUE;

    SELECT max(height)from testWHERE id = :b1AND acc_date = (SELECT MAX(acc_date)

    FROM testWHERE id = :b1);

    MAX(HEIGHT)-----------1480603530

    Elapsed: 00:00:00.12

    Execution Plan----------------------------------------------------------

    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=17)1 0 SORT (AGGREGATE)

    2 1 INDEX (RANGE SCAN) OF 'TEST_IDX' (NON-UNIQUE) (Cost=2 Card=1Bytes=17)3 2 SORT (AGGREGATE)4 3 FIRST ROW (Cost=2 Card=6 Bytes=60)5 4 INDEX (RANGE SCAN (MIN/MAX)) OF 'TEST_IDX' (NON-UNIQUE)

    (Cost=2 Card=1060)

    tkprof gek1_ora_16520.trc gek1_ora_16520.out explain=scott/tiger sort=exeelasys=no

    call count cpu elapsed disk query currentrows------- ------ -------- ---------- ---------- ---------- --------------------

    Parse 1 0.00 0.00 0 0 00Execute 1 0.00 0.00 0 2 00Fetch 2 0.00 0.00 0 2 01------- ------ -------- ---------- ---------- ---------- --------------------total 4 0.00 0.00 0 4 0

  • 8/2/2019 1 Good Oracle SQL Query Tuning Hints

    7/13

    1

    With Inline View

    VARIABLE b1 NUMBERexec :b1 := 10

    SELECT heightFROM (SELECT height

    FROM testWHERE id = :b1

    ORDER BY id DESC, acc_date DESC, height DESC)WHERE ROWNUM = 1;

    HEIGHT----------1480603530

    Execution Plan----------------------------------------------------------

    0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=13)1 0 COUNT (STOPKEY)2 1 VIEW (Cost=2 Card=6 Bytes=78)3 2 INDEX (RANGE SCAN DESCENDING) OF 'TEST_IDX' (NON-UNIQUE)

    (Cost=2 Card=6 Bytes=102)

    tkprof gek1_ora_16521.trc gek1_ora_16521.out explain=scott/tiger sort=exeelasys=no

    call count cpu elapsed disk query currentrows

    ------- ------ -------- ---------- ---------- ---------- --------------------Parse 1 0.03 0.06 2 41 00Execute 1 0.00 0.00 0 0 00Fetch 2 0.00 0.00 0 2 01------- ------ -------- ---------- ---------- ---------- --------------------total 4 0.03 0.06 2 43 01

    Example 2 (Replace Subquery for MAX)

    Original Query from a trace session:

    SELECT switch_time,rat_idFROM tariffWHERE effdate = (SELECT MAX(effdate)

    FROM tariffWHERE effdate

  • 8/2/2019 1 Good Oracle SQL Query Tuning Hints

    8/13

    AND weekday = :b2AND t_id = :b3)

    AND TO_CHAR(switch_time,'HH24:MI')

  • 8/2/2019 1 Good Oracle SQL Query Tuning Hints

    9/13

    tkprof xyz.trc xyz.out explain=user/pwd sort=exeela sys=no

    call count cpu elapsed disk query currentrows------- ------ -------- ---------- ---------- ---------- ----------

    ----------Parse 1 0.00 0.00 0 0 00Execute 1 0.00 0.00 0 0 00Fetch 2 0.01 0.00 0 38 84------- ------ -------- ---------- ---------- ---------- --------------------total 4 0.01 0.00 0 38 84

    Misses in library cache during parse: 0Optimizer goal: CHOOSE

    With Inline View

    VARIABLE b1 VARCHAR2(19)exec :b1 := '07.04.2005:13:30:31'

    VARIABLE b2 NUMBERexec :b2 := 2

    VARIABLE b3 NUMBERexec :b3 := 317

    SELECT switch_time, rat_iDFROM (SELECT switch_time, rat_idFROM tariffWHERE effdate

  • 8/2/2019 1 Good Oracle SQL Query Tuning Hints

    10/13

    5 4 INDEX (RANGE SCAN) OF 'PK_TARIFF' (UNIQUE) (Cost=2 Card=

    TKPROF:

    tkprof xyz.trc xyz.out explain=user/pwd sort=exeela sys=no

    call count cpu elapsed disk query currentrows------- ------ -------- ---------- ---------- ---------- --------------------Parse 1 0.00 0.00 0 0 00Execute 1 0.00 0.00 0 0 00Fetch 2 0.00 0.00 0 19 44------- ------ -------- ---------- ---------- ---------- --------------------

    total 4 0.00 0.00 0 19 44

    Misses in library cache during parse: 0Optimizer goal: CHOOSE

    Example 3 (cannot have join with CONNECT BY)

    Have you ever tried to join to a hierarchical query (a query using CONNECT BY and PRIOR)

    only to get this message:

    ORA-01437: cannot have join with CONNECT BY

    One of the limitations of hierarchical queries is that you cannot join to them. However, theare often times you would like to join to them anyway. For instance, if the hierarchy table

    only has surrogate keys, and you would like to display the real value. This tip shows howyou can use "Inline Views" to join tables to a hierarchical query.

    SELECT level, LPAD(' ',2*level-2)||ename ename, empno, mgr, dept.deptno,dept.dnameFROM emp, deptWHERE emp.deptno = dept.deptnoCONNECT BY PRIOr empno = mgrSTART WITH empno = 7839;

    ORA-01437: cannot have join with CONNECT BY

    SELECT E.emplevel, SUBSTR(E.ename,1,15) "ENAME", E.empno, dept.deptno,dept.dnameFROM dept, (SELECT level emplevel, LPAD(' ',2*level-2)||ename ename, empn

    mgr, deptnoFROM empCONNECT BY PRIOR empno = mgrSTART WITH empno = 7839) E

  • 8/2/2019 1 Good Oracle SQL Query Tuning Hints

    11/13

    WHERE E.deptno = dept.deptno/

    EMPLEVEL ENAME EMPNO DEPTNO DNAME---------- --------------- ---------- ---------- --------------

    1 KING 7839 10 ACCOUNTING2 CLARK 7782 10 ACCOUNTING3 MILLER 7934 10 ACCOUNTING2 JONES 7566 20 RESEARCH3 SCOTT 7788 20 RESEARCH4 ADAMS 7876 20 RESEARCH3 FORD 7902 20 RESEARCH4 SMITH 7369 20 RESEARCH2 BLAKE 7698 30 SALES3 ALLEN 7499 30 SALES3 WARD 7521 30 SALES3 MARTIN 7654 30 SALES3 TURNER 7844 30 SALES3 JAMES 7900 30 SALES

    Example 3 (ROWNUM 1 Problem)

    A rownum restriction starting with 1 works:

    ROWNUM does not work for ranges that don't start at 1.

    A ROWNUM restriction starting with 1 works:

    SELECT ROWNUM,ename from emp WHERE ROWNUM BETWEEN 1 and 3/

    ROWNUM ENAME---------- ----------

    1 SMITH2 ALLEN3 WARD

    However, if you try to use a range it will not work. For example:

    SELECT ROWNUM,ename from emp WHERE ROWNUM BETWEEN 2 and 3/

    no rows selected

    Using an Inline View to get around this limitation:

    SELECT t1.rn, t1.enameFROM (SELECT ROWNUM rn, ename

    FROM emp) t1WHERE t1.rn BETWEEN 2 and 3/

    The main trick to this query is the "internal" select statement. This select statement in the

  • 8/2/2019 1 Good Oracle SQL Query Tuning Hints

    12/13

    from clause, basically does a full query of the table, then returns the values (along with thpsuedo-column ROWNUM) to the "outside" query. The outside query can then operate on

    the results of the internal query. In order to access the internal query's columns from theexternal query, you need to give the internal query an alias ("t1" highlighted below): This

    allows you to refer to the columns using the "t1" (highlighted below): Since "ROWNUM" is

    psuedo-column and therefore a reserved word, you need to alias that column in the internquery in order to refer to it in the outside query:

    Example 4 (ROWNUM and ORDER BY Problem, TOP-NQueries)

    The following query form is almost wrong:

    select * from emp where ROWNUM

  • 8/2/2019 1 Good Oracle SQL Query Tuning Hints

    13/13

    Example 5 (Pagination with ROWNUM)

    Pagination with ROWNUM can be used to get rows N thru M of a result set. The general for

    of this is as follows:

    SELECT *FROM (SELECT a.*, ROWNUM rnFROM (enter your query here) aWHERE ROWNUM = :MIN_ROW;

    SELECT *FROM (SELECT a.*, ROWNUM rn

    FROM (SELECT * FROM emp) aWHERE ROWNUM = 2;

    EMPNO ENAME JOB MGR HIREDATE SAL COMMDEPTNO RN---------- ---------- --------- ---------- --------- ---------- -------------------- ----------

    7499 ALLEN SALESMAN 7698 20-FEB-81 1600300 30 2

    7521 WARD SALESMAN 7698 22-FEB-81 1250500 30 3

    7566 JONES MANAGER 7839 02-APR-812975 20 4

    7654 MARTIN SALESMAN 7698 28-SEP-81 12501400 30 5

    7698 BLAKE MANAGER 7839 01-MAY-812850 30 6