Sie sind auf Seite 1von 53

Presentation Outline

SQL Writing Process

SQL Standards
Using Indexes The Optimizer FROM, WHERE Clauses EXPLAIN SQL Trace Sub-Selects and Joins Tips and Tricks
1

SQL Writing Process


Step 1: What information do I need? Columns Step 2: Where is it? Tables Step 3: Write SQL:
SELECT columns FROM tables WHERE ... (joins, filters, subqueries)

I'M FINISHED!

SQL Writing Process


YOU'RE NOT FINISHED YET! You've got the results you want, but at what cost? There are many, many ways to get the right results, but only one is the fastest way1000-to-1 improvements are attainable! Inefficient SQL can dramatically degrade the performance of the entire system

Developers and DBAs must work together to tune the database and the application

SQL Standards
Why are SQL standards important?

Maintainability, readability
Performance: If SQL is the same as a (recently) executed statement, it can be re-used instead of needing to be reparsed

SQL Standards
Question: which of these statements are the same?

A. SELECT LNAME FROM EMP WHERE EMPNO = 12;


B. SELECT lname FROM emp WHERE empno = 12; C. SELECT lname FROM emp WHERE empno = :id; D. SELECT lname FROM
WHERE empno = 12; emp

SQL Standards
Answer: None

Whitespace, case, bind variables vs. constants all matter


Using standards helps to ensure that equivalent SQL can be reused.

Tables Used in the Examples


DEPT deptno dname loc
EMP empno mgr job deptno fname lname comm hiredate grade sal
7

SALGRADE grade losal hisal

SQL Standards: Example


SELECT E.empno, D.dname FROM emp E, dept D WHERE AND OR E.deptno = D.deptno (D.deptno = :vardept E.empno = :varemp); Keywords upper case and left-aligned Columns on new lines Use std. table aliases

Separate w/ one space


Use bind variables AND/OR on new lines

No space before/after parentheses

Indexes: What are they?


An index is a database object used to speed retrieval of rows in a table. The index contains only the indexed value--usually the key(s)--and a pointer to the row in the table. Multiple indexes may be created for a table Not all indexes contain unique values Indexes may have multiple columns

Indexes and SQL


If a column appears in a WHERE clause it is a candidate for being indexed. If a column is indexed the database can used the index to find the rows instead of scanning the table. If the column is not referenced properly, however, the database may not be able to used the index and will have to scan the table anyway. Knowing what columns are and are not indexed can help you write more efficient SQL
10

Example: Query without Index


No index exists for column EMPNO on table EMP, so a table scan must be performed:
Table: EMP SELECT * FROM emp WHERE empno = 8
empno 4 9 1 3 5 2 7 8 6 fname lisa jackie john larry jim mary harold mark gene lname... baker miller larson jones clark smith simmons burns harris

11

Example: Query with Index


Column EMPNO is indexed, so it can be used to find the requested row:
SELECT * FROM emp WHERE empno = 8 Index: PK_EMP EMP (EMPNO)
1, 4

Table: EMP
5 5, 9 empno 4 9 1 3 5 2 7 8 6 fname lisa jackie john larry jim mary harold mark gene lname ... baker miller larson jones clark smith simmons burns harris

1 2

7 8 9

12

Indexes: Caveats
Sometimes a table scan cannot be avoided Not every column should be indexed--there is performance overhead on Inserts, Updates, Deletes Small tables may be faster with a table scan Queries returning a large number (> 5-20%) of the rows in the table may be faster with a table scan

13

Indexes: Column Order


Example: Index on (EMPNO, DEPTNO)
SELECT * FROM emp WHERE deptno = 10;

SELECT FROM WHERE AND

* emp empno > 0 deptno = 10;

Question: which of these statements will use the index?


14

Indexes: Column Order


First statement will not use index. Second statement will use index.

Must use the leading column of the index.

15

Indexes: Functions
Question: which of these statements will use the index?
SELECT FROM WHERE ... WHERE * emp TRUNC(hiredate) = TRUNC(SYSDATE); fname || lname = 'MARYSMITH';

SELECT * FROM emp WHERE hiredate BETWEEN TRUNC(SYSDATE) AND TRUNC(SYSDATE)+1 ... WHERE fname = 'MARY' AND lname = 'SMITH';
16

Indexes: Functions
First statement will not use index. Second statement will use index. Using a function, calculation, or other operation on an indexed column disables the use of the Index.

17

Indexes: NOT
Question: which of these statements will use the index?
SELECT FROM WHERE ... ... * dept deptno != 0; deptno NOT = 0; deptno IS NOT NULL;

SELECT * FROM dept WHERE deptno > 0;

18

Indexes: NOT
First statement will not use index. Second statement will use index. Using NOT excludes indexed columns.

19

The Optimizer
The WHERE/FROM rules on the following pages apply to the Rule-based optimizer (Oracle). If the Cost-based Optimizer is used, Oracle will attempt to reorder the statements as efficiently as possible (assuming statistics are available). DB2 and Sybase use only a Cost-based optimizer The Optimizer's access paths can be overridden in Oracle and Sybase (not DB2)

20

The Optimizer: Hints


Return the first rows in the result set as fast as possible:
SELECT /*+ FIRST_ROWS */ empno FROM emp E dept D, WHERE E.deptno = D.deptno;

Force Optimizer to use index IDX_HIREDATE:


SELECT /*+ INDEX (E idx_hiredate) */ empno FROM emp E WHERE E.hiredate > TO_DATE('01-JAN-2000');

21

FROM Clause: Driving Table


Specify the driving table last in the FROM Clause:
SELECT * FROM dept D, -- 10 rows emp E -- 1,000 rows WHERE E.deptno = D.deptno;
SELECT * FROM emp E, -- 1,000 rows dept D -- 10 rows WHERE E.deptno = D.deptno;

Driving table is EMP

Driving table is DEPT

22

FROM Clause: Intersection Table


When joining 3 or more tables, use the Intersection table (with the most shared columns) as the driving table:
SELECT * FROM dept D, salgrade S, emp E WHERE E.deptno = D.deptno AND E.grade = S.grade;

EMP shares columns with DEPT and SALGRADE, so use as the driving table

23

WHERE: Discard Early


Use WHERE clauses first which discard the maximum number of rows:
SELECT FROM WHERE AND * emp E E.empno IN (101, 102, 103) E.deptno > 10;

3 rows 90,000 rows

24

WHERE: USING AND


Question: which of these statements will more effective?
SELECT FROM WHERE AND * emp E E.sal > 50000 25 > (SELECT COUNT(*) FROM emp M WHERE M.mgr = E.empno)

SELECT * FROM emp E WHERE 25 > (SELECT COUNT(*) FROM emp M WHERE M.mgr = E.empno) AND E.sal > 50000
25

WHERE: USING AND


When using an "AND" sub query, place it first. It will more cost effective.

26

WHERE: USING OR
Question: which of these statements will more effective?
SELECT * FROM emp E WHERE 25 > (SELECT COUNT(*) FROM emp M WHERE M.mgr = E.empno) OR E.sal > 50000 SELECT FROM WHERE OR * emp E E.sal > 50000 25 > (SELECT COUNT(*) FROM emp M WHERE M.mgr = E.empno)
27

WHERE: USING OR
Using an "OR" sub query, place it last. It will more cost effective.

28

WHERE: Filter First, Join Last


When Joining and Filtering, specify the Filter condition first, Joins last.
SELECT * FROM emp E, dept D WHERE (E.empno = 123 OR D.deptno > 10) AND E.deptno = D.deptno;

Filter criteria Join criteria

29

Subqueries: IN vs. EXISTS


Question: which of these statements will more effective?
SELECT E.* FROM emp E WHERE E.deptno IN ( SELECT D.deptno FROM dept D WHERE D.dname = 'SALES'); SELECT * FROM emp E WHERE EXISTS SELECT FROM WHERE AND

( 'X' dept D D.deptno = E.deptno D.dname = 'SALES');


30

Subqueries: IN vs. EXISTS


IN : Both tables are scanned. EXITS : Only outer table is scanned;Sub query used the index.

Use EXISTS instead of IN in sub queries.

31

Subquery vs. Join


Question: which of these statements will more effective?
SELECT * FROM emp E WHERE E.deptno IN ( SELECT D.deptno FROM dept D WHERE D.dname = 'SALES'); SELECT E.* FROM emp E, dept D WHERE D.dname = 'SALES' AND D.deptno = E.deptno; 32

Subquery vs. Join


IN : Both tables are scanned. JOIN : Only one table is scanned,other use index.

Use Join instead of Sub query.

33

Join vs. EXISTS


Best performance depends on subquery/driving table:
SELECT * FROM emp E WHERE EXISTS SELECT FROM WHERE AND EXISTS: better than Join if the number of matching rows in DEPT is small

( 'X' dept D D.deptno = E.deptno D.dname = 'SALES');

SELECT E.* FROM emp E, dept D WHERE D.dname = 'SALES' AND D.deptno = E.deptno;
34

JOIN: better than Exists if the number of matching rows in DEPT is large

Explain
Display the access path the database will use (e.g., use of indexes, sorts, joins, table scans) Oracle: Sybase: DB2: EXPLAIN SHOWPLAN EXPLAIN

Oracle Syntax:
EXPLAIN PLAN SET STATEMENT_ID = 'statement id' INTO PLAN_TABLE FOR

statement

Requires Select/Insert privileges on PLAN_TABLE


35

Explain
Example 1: IN subquery
SELECT * FROM emp E WHERE E.deptno IN ( SELECT D.deptno FROM dept D WHERE D.dname = 'SALES');

Result:

3 joins MERGE JOIN 1 dynamic view SORT (JOIN) 2 table scans TABLE ACCESS (FULL) OF EMP 3 sorts SORT (JOIN) VIEW SORT (UNIQUE) TABLE ACCESS (FULL) OF DEPT
36

Explain
Example 2: "EXISTS" subquery
SELECT * FROM emp e WHERE EXISTS SELECT FROM WHERE AND

( 'x' dept d d.deptno = e.deptno d.dname = 'SALES');


1 table scan 1 index scan 1 index access

Result:

FILTER TABLE ACCESS (FULL) OF EMP TABLE ACCESS (BY INDEX ROWID) OF DEPT INDEX (UNIQUE SCAN) OF PK_DEPT (UNIQUE)
37

Explain
Example 3: Join (no subquery)
SELECT E.* FROM emp E, dept D WHERE D.dname = 'SALES' AND D.deptno = E.deptno;

Result:

1 table scan 1 index scan 1 index access

NESTED LOOPS TABLE ACCESS (FULL) OF EMP TABLE ACCESS (BY INDEX ROWID) OF DEPT INDEX (UNIQUE SCAN) OF PK_DEPT (UNIQUE)

38

SQL Trace
Use SQL Trace to determine the actual time and resource costs for for a statement to execute. Step 1: ALTER SESSION SET SQL_TRACE TRUE; Step 2: Execute SQL to be traced:
SELECT E.* FROM emp E, dept D WHERE D.dname = 'SALES' AND D.deptno = E.deptno;

Step 3: ALTER SESSION SET SQL_TRACE FALSE;


39

SQL Trace
Step 4: Trace file is created in <USER_DUMP_DEST> directory on the server (specified by the DBA).

Step 5: Run TKPROF (UNIX) to create a formatted output file:


tkprof echd_ora_15319.trc $HOME/prof.out table=plan_table explain=dbuser/passwd

Trace file Formatted output file destination for Explain user/passwd for Explain

40

SQL Trace
Step 6: view the output file:
... SELECT E.* FROM emp E, dept D WHERE D.dname = 'SALES' AND D.deptno = E.deptno; call count ------- -----Parse 1 Execute 1 Fetch 2 ------- -----total 4

TIMED_STATISTICS must be turned on to get these values


rows ---------0 0 6 ---------6

cpu elapsed disk query current -------- ---------- ---------- ---------- ---------0.00 0.00 0 0 0 0.00 0.00 0 0 0 0.00 0.00 4 19 3 -------- ---------- ---------- ---------- ---------0.00 0.00 4 19 3

Misses in library cache during parse: 0 Optimizer goal: CHOOSE Parsing user id: 62 (PMARKS) Rows ------6 14 14 14 Row Source Operation --------------------------------------------------NESTED LOOPS TABLE ACCESS FULL EMP TABLE ACCESS BY INDEX ROWID DEPT INDEX UNIQUE SCAN (object id 4628)

EXPLAIN output

41

Tips and Tricks: UNION ALL


Use UNION ALL instead of UNION if there are no duplicate rows (or if you don't mind duplicates):
SELECT * FROM emp UNION SELECT * FROM emp_arch; SELECT * FROM emp UNION ALL SELECT * FROM emp_arch;

UNION: requires sort

UNION ALL: no sort

42

Tips and Tricks: HAVING vs. WHERE


With GROUP BY, use WHERE instead of HAVING (if the filter criteria does not apply to a group function):
SELECT deptno, AVG(sal) FROM emp GROUP BY deptno HAVING deptno IN (10, 20); SELECT deptno, AVG(sal) FROM emp WHERE deptno IN (10, 20) GROUP BY deptno;
43

HAVING: rows are filtered after result set is returned

WHERE: rows are filtered first--possibly far fewer to process

Tips and Tricks: EXISTS vs DISTINCT


Use EXISTS instead of DISTINCT to avoid implicit sort (if the column is indexed):
SELECT DISTINCT e.deptno, e.lname FROM dept d, emp e WHERE d.deptno = e.deptno;

DISTINCT: implicit sort is performed to filter duplicate rows

SELECT e.deptno, e.lname EXISTS: no sort FROM emp e WHERE EXISTS ( SELECT 'X' FROM dept d WHERE d.deptno = e.deptno);
44

Tips and Tricks: Consolidate SQL


Select from Sequences and use SYSDATE in the statement in which they are used:
SELECT SYSDATE INTO :vardate FROM dual; FROM dual;

SELECT arch_seq.NEXTVAL INTO :varid

INSERT INTO archive BEFORE: 3 statements VALUES (:vardate, :varid, ...) are used to perform 1 Insert
INSERT INTO emp_archive VALUES (SYSDATE, emp_seq.NEXTVAL, ...) AFTER: only 1 statement is needed
45

Tips and Tricks: Consolidate SQL


Consolidate unrelated statements using outer-joins to the the DUAL (dummy) table:
SELECT dname FROM dept WHERE deptno = 10; SELECT lname FROM emp WHERE empno = 7369;

SELECT d.dname, e.lname FROM dept d, emp e, dual x WHERE d.deptno AND e.empno AND NVL('X', AND NVL('X',

BEFORE: 2 round-trips

AFTER: only 1 round-trip

(+) = 10 (+) = 7369 x.dummy) = NVL('X', e.ROWID (+)) x.dummy) = NVL('X', d.ROWID (+));
46

Tips and Tricks: COUNT


Use COUNT(*) instead of COUNT(column):
SELECT COUNT(empno) FROM emp;

SELECT COUNT(*) FROM emp;

~ 50% faster

47

Tips and Tricks: Self-Join


Use a self-join (joining a table to itself) instead of two queries on the same table:
SELECT mgr INTO :varmgr FROM emp WHERE deptno = 10; LOOP... SELECT mgr, lname FROM emp WHERE mgr = :varmgr; SELECT E.mgr, E.lname FROM emp E, emp M WHERE M.deptno = 10 AND E.empno = M.mgr;

BEFORE: 2 round-trips

AFTER: only 1

48

Tips and Tricks: ROWNUM


Use the ROWNUM pseudo-column to return only the first N rows of a result set. (For example, if you just want a sampling of data):
SELECT * FROM emp WHERE ROWNUM <= 10; Returns only the first 10 employees in the table, in no particular order

49

Tips and Tricks: ROWID


The ROWID pseudo-column uniquely identifies a row, and is the fastest way to access a row:
CURSOR retired_emp_cur IS Instead of selecting the SELECT ROWID key column(s), ROWID is FROM emp used to identify the row WHERE retired = 'Y'; for later use ... FOR retired_emp_rec IN retired_emp_cur LOOP SELECT fname || ' ' || lname INTO :printable_name FROM emp WHERE ROWID = retired_emp_rec.ROWID; ...
50

Tips and Tricks: Sequences


Use a Sequence to generate unique values for a table:
SELECT INTO FROM ... INSERT VALUES MAX(empno) :new_empno emp; INTO emp (:new_empno + 1, ...); MAX(empno) requires a sort and an index scan INSERT could fail with a Duplicate error if someone else gets there first

Using a Sequence INSERT INTO emp VALUES (emp_seq.NEXTVAL, ...); ensures that you always have a unique number, or and does not require any SELECT emp_seq.NEXVAL table reads INTO :new_empno FROM dual;
51

THANK YOU

52

Tips and Tricks: Cartesian Products


Avoid Cartesian products by ensuring that the tables are joined on all shared keys:
SELECT FROM * dept, -- 10 rows salgrade, -- 20 rows emp; -- 1,000 rows

10 * 1000 * 20 = 200,000 rows


SELECT FROM * dept, -- 10 rows salgrade, -- 20 rows emp -- 1,000 rows E.deptno = D.deptno E.grade = S.grade;
53

WHERE AND

1,000 rows

Das könnte Ihnen auch gefallen