Академический Документы
Профессиональный Документы
Культура Документы
By Puneet Goenka
It is much harder to write efficient SQL than it is to write functionally correct SQL A SQL choice is correct only if it produces the right result in the shortest possible amount of time, without impeding the performance of any other system resources.
For example the following two select statements are NOT the same:
Since the CLIENT_NUMBER is likely to be different for every execution, we will almost never find a matching statement in the Shared Pool and consequently the statement will have to be reparsed every time Consider the following approach
SELECT FIRST_NAME, LAST_NAME FROM Client WHERE CLIENT_NUM = :Client_Num
You do not need to create a new cursor or re-parse the SQL statement if the value of the bind variable changes. Also, if another session executes the same statement, it is likely to find them in the Shared Pool, since the name of the bind variable does not change from execution to execution.
cursor accounts_cur is select acct_no, currency, branch Rowid acct_rowid, From account where . . . . for acct_rec in accounts_cur loop update account set where rowid = acct_rec.acct_rowid; end loop;
8
10
12
Using Joints in Place of EXISTS for Unique Scan Indexes and small tables
In general join tables rather than specifying sub-queries for them such as the following:
select acct_ID, currency, branch from account where exists (select 1 from branch where code = branch and def_curr = '001') With join select acct_ID,currency, branch from account A, branch B where b.code = A.branch and A.def_curr = '001'
13
If more than 52%, this percentage defers from table to table and depends on the physical I/O, of the table retrieved a full table scan is better.
16
19
PositionofJoinsintheWHEREClause
Table joins should be written first before any condition of WHERE clause. And the conditions which filter out the maximum records should be placed at the end after the joins as the parsing is done from BOTTOM to TOP. Least Efficient : SELECT . . . .
FROM EMP E WHERE SAL > 50000 AND JOB = CLERK AND 25 < (SELECT COUNT(*) FROM EMP WHERE MGR = E.EMPNO);
20
Most Efficient :
SELECT . . . . FROM EMP E WHERE 25 < (SELECT COUNT(*) FROM EMP WHERE MGR = E.EMPNO ) AND SAL > 50000 AND JOB = CLERK;
21
SidebySideComparisonofJoinMethods
Nested Loops Join Cluster Join When can be used: Any join Equi joins on complete cluster key of clustered
tables only
Sort-Merge Join
Hash Join
use_nl
Use_merge
use_hash
Resource concerns: CPU Storage Disk I/O init.ora parameters:None hash_join_enabled block_ hash_area_size
Memory
db_file_multi
ORACLE parser always processes table names from right to left, so the table name you specify last (driving table) is actually the first table processed. If you specify more than one table in a FROM clause of a SELECT statement, you must choose the table containing the lowest number of rows as the driving table. When ORACLE processes multiple tables, it uses an internal sort/merge procedure to join those tables. First, it scans and sorts the first table (the one specified last in the FROM clause). Next, it scans the second table (the one prior to the last in the FROM clause) and merges all of the rows retrieved from the second table with those retrieved from the first table. For example: Table TABA has 16,384 rows. Table TABB has 1 row. SELECT COUNT(*) FROM TABA, TABB
SELECT COUNT(*) FROM TABB, TABA
If three tables are being joined, select the intersection table as the driving table. The intersection table is the table that has many tables dependent on it. E.g.. The EMP table represents the intersection between the LOCATION table and the CATEGORY table. SELECT . . . FROM LOCATION L, CATEGORY C, EMP E WHERE E.EMP_NO BETWEEN 1000 AND 2000 AND E.CAT_NO = C.CAT_NO AND E.LOCN = L.LOCN is more efficient than this next example: SELECT . . . FROM EMP E, LOCATION L, CATEGORY C WHERE E.CAT_NO = C.CAT_NO AND E.LOCN = L.LOCN AND E.EMP_NO BETWEEN 1000 AND 2000
24
But the following statement: Select * From acc_txn Where acc_txn_ref_no = 119990012890 Will be processed as: Select * From acc_txn Where acc_txn_ref_no = to_number(119990012890 )
26
SELECT COUNT(DECODE(DEPT_NO,0020, X, NULL)) D0020_COUNT, COUNT(DECODE(DEPT_NO,0030,X,NULL)) D0030_COUNT, SUM(DECODE(DEPT_NO,0020, SAL, NULL)) D0020_SAL, SUM(DECODE(DEPT_NO, 0030, SAL, NULL)) D0030_SAL FROM EMP WHERE ENAME LIKE SMITH%; Similarly, DECODE can be used in GROUP BY or ORDER BY clause effectively.
28
To improve performance, minimize the number of table lookups in queries, particularly if your statements include sub-query SELECTs or multi-column UPDATEs. For example: Least Efficient : SELECT TAB_NAME FROM TABLES WHERE TAB_NAME = (SELECT TAB_NAME FROM TAB_COLUMNS WHERE VERSION = 604) AND DB_VER = (SELECT DB_VER FROM TAB_COLUMNS WHERE VERSION = 604)
29
Most Efficient :
SELECT TAB_NAME FROM TABLES WHERE (TAB_NAME,DB_VER)= (SELECT TAB_NAME, DB_VER FROM TAB_COLUMNS WHERE VERSION = 604)
30
32
Do Not Use: Use Select * from Account Where substr(ac_acct_no,1,1) = 9 Use: Select * from Account Where ac_acct_no like 9% Do Not Use: Select * From fin_trxn Where ft_trxn_ref_no != 0 Use: Select * From fin_trxn Where ft_trxn_ref_no > 0
33
Use: Select * From account Where ac_type = sav And ac_branch = sav001
Use: Select * From CLIENT Where CUT_OFF_DATE >= trunc(sysdate) and CUT_OFF_TIME < trunc(sysdate) + 1
34
Do Not Use: Select * From acct_trxn Where to_char(at_value_date,yyyymmdd) > to_char(sysdate,yyyymmdd) Use: Select * From acct_trxn Where at_value_date >= trunc(sysdate) + 1
35
Do Not Use: Select * From acct_trxn Where to_char(at_value_date,yyyymmdd) < to_char(sysdate,yyyymmdd) Use: Select * From acct_trxn Where at_value_date < trunc(sysdate)
36
Do Not Use: Select * From acct_trxn Where to_char(at_value_date,yyyymmdd) >= to_char(sysdate,yyyymmdd) Use: Select * From acct_trxn Where at_value_date >= trunc(sysdate)
37
Do Not Use: Select * From acct_trxn Where to_char(at_value_date,yyyymmdd) <= to_char(sysdate,yyyymmdd) Use: Select * From acct_trxn Where at_value_date < trunc(sysdate) + 1 Do Not Use: Select count( *) From BROKER Use: Select count(PRIMARY_KEY or a non null INDEX column or 1 ) From Broker
38
AvoidUsingSELECT*Clauses
The dynamic SQL column reference (*) gives you a way to refer to all of the columns of a table. Do not use the * feature because it is very inefficient -- the * has to be converted to each column in turn. The SQL parser handles all the field references by obtaining the names of valid columns from the data dictionary and substitutes them on the command line, which is time consuming.
39
3 rows selected. Execution Plan ---------------------------------------------------------0 SELECT STATEMENT Optimizer=CHOOSE (Cost=3 Card=10 Bytes=170) 11 0 SORT (ORDER BY) (Cost=3 Card=10 Bytes=170) 2 1 TABLE ACCESS (FULL) OF AQUATIC_ANIMAL (Cost=1 Card=10 Bytes=170)
40
SQL*Plus does execute the query. If a query generates a lot of I/O and consumes a lot of CPU, you wont want to kick it off just to see the execution plan. In that case use following : SQL> SET AUTOTRACE TRACEONLY EXPLAIN you are through using autotrace, you can turn the feature off by issuing the SET AUTOTRACE OFF command.
41