tối ưu-cau-lệnh-oracle-sql
DESCRIPTION
Toi uu hoa cau lenh SQLTRANSCRIPT
T i u câu l nh Oracle SQL – ph n 1: n nố ư ệ ầ ề t ng c b nả ơ ảT i u câu l nh là ch đ nâng cao, vì v y đ có th hi u đ c các k thu t t i u câu l nh Oracle SQL, chúng taố ư ệ ủ ề ậ ể ể ể ượ ỹ ậ ố ư ệ c n tìm hi u m t s khái ni m c b n liên quan đ n lĩnh v c này, bài vi t này không nh m m c đích chuy n t i toànầ ể ộ ố ệ ơ ả ế ự ế ằ ụ ể ả b ki n th c liên quan đ n t i u câu l nh SQL, bài vi t ch trình bày nh ng khái ni m quan tr ng nh t, đ giúpộ ế ứ ế ố ư ệ ế ỉ ữ ệ ọ ấ ủ nh ng đ c gi đã am hi u ki n trúc Oracle có th hi u đ c. ữ ọ ả ể ế ể ể ượ
N n t ng c b n ề ả ơ ả
Trình t x lý câu l nh SQLự ử ệ
Oracle Server x lý câu l nh SQL theo trình t các b c sau:ử ệ ự ướ
1. Open o Ng m đ nh khai báo và kh i t o Cursor cho câu l nh SQLầ ị ở ạ ệ
2. Parse o Ki m tra câu l nh đã th c hi n tr c đó ch a, n u có thì chuy n sang th c hi n b c 3ể ệ ự ệ ướ ư ế ể ự ệ ướo Phân tích và ki m tra cú pháp câu l nh SQLể ệo Ki m tra tính h p l và quy n truy c p đ n các đ i t ng d li u câu l nh tham chi u t iể ợ ệ ề ậ ế ố ượ ữ ệ ệ ế ớo Xác đ nh “ị s đ th c thi câu l nh SQLơ ổ ự ệ ” t i u nh tố ư ấ
3. Bind o Tìm và gán giá tr cho các ị bind-variable n u cóế
4. Execute o Th c thi các b c mô t trong “s đ th c thi câu l nh SQL”ự ướ ả ơ ồ ự ệ
5. Fetch o Chuy n k t qu v n i g i th c thi l nhể ế ả ề ơ ọ ự ệ
6. Close o Ng m đ nh đóng Cursor cho câu l nhầ ị ệ
Trong các giai đo n x lý trên, giai đo n ạ ử ạ Parse chi m nhi u th i gian nh t.ế ề ờ ấ
Công c t i u câu l nh SQL*Plus AUTOTRACE ụ ố ư ệ
Ch c năngứ
Thu th p thông tin mô t quá trình th c thi câu l nh SQL, th ng đ c dùng đ t i u câu l nh.ậ ả ự ệ ườ ượ ể ố ư ệ
Cài đ tặ
Đ s d ng đ y đ ch c năng c a công c này, c n t o Table tên PLAN_TABLE và gán nhóm quy n PLUSTRACE choể ử ụ ầ ủ ứ ủ ụ ầ ạ ề tài kho n mu n s d ng công c :ả ố ử ụ ụ
1. Dùng $ORACLE_HOME/rdbms/admin/utlxplan.sql đ t o PLAN_TABLEể ạ2. Dùng tài kho n DBA ch y mã l nh $ORACLE_HOME/sqlplus/admin/plustrce.sql đ t o nhóm quy nả ạ ệ ể ạ ề
PLUSTRACE.
Ví dụ
Quan sát k t qu t mã d n trên, ta th y công c AUTOTRACE g i v ba nhóm thông tin sau:ế ả ừ ẫ ấ ụ ử ề
1. K t qu câu l nhế ả ệ2. S đ trình t th c thi câu l nhơ ồ ự ự ệ3. Thông tin th ng kê chi phí tài nguyên c n dùng đ th c thi câu l nhố ầ ể ự ệ
Cách s d ngử ụ
Cú pháp l nhệ Ý nghĩa
SET AUTOTRACE ON B t tính năng thu th p và hi n th thông tin đ y đậ ậ ể ị ầ ủ
SET AUTOTRACE OFF T t tính năng thu th p và hi n th thông tinắ ậ ể ị
SET AUTOTRACE ON EXPLAIN Hi n th k t qu và EP câu l nhể ị ế ả ệ
SET AUTOTRACE ON STATISTICS Hi n th k t qu và thông tin th ng kê câu l nhể ị ế ả ố ệ
SET AUTOTRACE TRACEONLY Hi n th EP và thông tin th ng kê, không hi n th k t qu câu l nhể ị ố ể ị ế ả ệ
SET AUTOTRACE TRACEONLY EXPLAIN Ch hi n th EPỉ ể ị
SET AUTOTRACE TRACEONLY STATISTICS Ch hi n th th ng kêỉ ể ị ố
S đ th c thi câu l nh SQL ơ ồ ự ệ
S đ th c thi câu l nh SQL (Execution Plan - EP) mô t th t các b c Oracle c n th c thi đ có đ c k t qu câuơ ồ ự ệ ả ứ ự ướ ầ ự ể ượ ế ả l nh nhanh nh t.ệ ấ
Ví d v EP c a câu l nh xem d li u trên hai Table CUSTOMERS và COUNTRIES:ụ ề ủ ệ ữ ệ
Oracle qui đ nh th t x lý các b c c a EP nh sau:ị ứ ự ử ướ ủ ư
1. B t đ u x lý t b c n m th t v phía bên ph i nh t, ti p theo là các b c đ ng k tr c nóắ ầ ử ừ ướ ằ ụ ề ả ấ ế ướ ứ ế ướ2. N u hai b c có cùng th t thì s x lý b c n m phía trên tr cế ướ ứ ự ẽ ử ướ ằ ướ
Nh v y, ta th y EP trên đ c x lý theo th t đ c đánh s bên ph i.ư ậ ấ ượ ử ứ ự ượ ố ả
M t EP đ y đ bao g m ba thành t sau: ph ng th c truy c p d li u, ph ng pháp JOIN và th t JOIN gi a haiộ ầ ủ ồ ố ươ ứ ậ ữ ệ ươ ứ ự ữ hay nhi u t p d li u.ề ậ ữ ệ
Ph ng th c truy c p d li uươ ứ ậ ữ ệ
Xác đ nh cách th c truy c p v t lý đ n t ng dòng d li u c a Table, ví du m t s ph ng pháp truy c p d li uị ứ ậ ậ ế ừ ữ ệ ủ ộ ố ươ ậ ữ ệ Table ph bi n:ổ ế
• Truy c p theo cách đ c t ng dòng c a Table ( TABLE ACCESS (FULL) ): đ c toàn b dòng d li u c a Tableậ ọ ừ ủ ọ ộ ữ ệ ủ đ đ i sánh d li u, vì v y n u Table có kích th c l n thì ph ng th c truy c p này s chi m nhi u chi phíể ố ữ ệ ậ ế ướ ớ ươ ứ ậ ẽ ế ề IO. Tuy nhiên, cách truy c p này cho phép chúng ta có th c u hình đ c nhi u kh i d li u (Block) cho m tậ ể ấ ọ ề ố ữ ệ ộ thao tác đ c và nhi u ti n trình cùng đ c m t lúc.ọ ề ế ọ ộ
• Truy c p d li u cây Index (INDEX SCAN): duy t cây Index theo giá tr khóa c n tìm, k t qu tìm đ c cóậ ữ ệ ệ ị ầ ế ả ượ l u giá tr ROWID c a dòng d li u ch a giá tr khóaư ị ủ ữ ệ ứ ị
• Truy c p d li u Table theo Index ( TABLE ACCESS (BY INDEX ROWID)): ph ng th c truy c p này bao g mậ ữ ệ ươ ứ ậ ồ hai b c tách bi t: ướ ệ
1. Duy t cây Index đ tìm ROWID t ng ng v i giá tr khóaệ ể ươ ứ ớ ị2. Đ c dòng d li u c a Table theo ROWID tìm đ c m t cách nhanh nh tọ ữ ệ ủ ượ ộ ấ
V i cách này, ta th y đ đ c đ c m t dòng d li u c a Table thì ph i t n ít nh t hai thao tác IO; m t thaoớ ấ ể ọ ượ ộ ữ ệ ủ ả ố ấ ộ tác IO đ c trên cây Index và m t thao tác IO đ c trên Table. Theo lu t chung thì cách này thích h p khi k tọ ộ ọ ậ ợ ế qu d li u c n tìm ít h n 5% kh i l ng d li u c a Table.ả ữ ệ ầ ơ ố ượ ữ ệ ủ
Ph ng pháp JOIN ươ
Xác đ nh ph ng pháp k t h p d li u gi a hai hay nhi u Table v i nhau, ví d v ph ng pháp JOIN gi a hai Tableị ươ ế ợ ữ ệ ữ ề ớ ụ ề ươ ữ T1 và T2:
1. NESTED LOOP JOIN: v i T1 là ớ outer-table, T2 là inner-table thì t ng dòng d li u c a Table T1 s k t h p soừ ữ ệ ủ ẽ ế ợ sánh v i t t c dòng d li u c a Table T2, k t qu tr v là t t c các dòng d li u th a đi u ki n so sánh.ớ ấ ả ữ ệ ủ ế ả ả ề ấ ả ữ ệ ỏ ề ệ
2. SORT MERGE JOIN: t p dòng d li u c a hai Table đ c s p theo th t tr c khi ng d ng thu t toánậ ữ ệ ủ ượ ắ ứ ự ướ ứ ụ ậ tr n trên chúng.ộ
Th t JOINứ ự
Xác đ nh th t JOIN khi câu l nh SQL có nhi u h n hai Table k t h p v i nhau, th t JOIN h p lý giúp gi m thi uị ứ ự ệ ề ơ ế ợ ớ ứ ự ợ ả ể d li u c n k t h p v i nhau mà v n đ t đ c k t qu đúng.ữ ệ ầ ế ợ ớ ẫ ạ ượ ế ả
Sau khi tìm hi u qua các khái ni m c b n, chúng ta th quay l i tìm hi u ý nghĩa EP c a câu l nh SQL trên:ể ệ ơ ả ử ạ ể ủ ệ
1. Oracle ch n ph ng pháp JOIN là NESTED LOOP đ th c hi n phép JOIN gi a hai Table.ọ ươ ể ự ệ ữ2. Oracle ch n CUSTOMERS đóng vai trò là outer-table, COUNTRIES là inner-tableọ3. B t đ u đ c t ng dòng d li u c a CUSTOMERS r i so trùng v i t t c dòng c a COUNTRIES, đi m l u ý làắ ầ ọ ừ ữ ệ ủ ồ ớ ấ ả ủ ể ư
giá tr COUNTRIES.COUNTRY_ID này đ c l y t cây Index COUNTRY_PKị ượ ấ ừ4. K t qu tr v là t t c nh ng dòng d li u th a đi u ki n so trùngế ả ả ề ấ ả ữ ữ ệ ỏ ề ệ
Đ n đây có th các b n th c m c là t i sao Oracle l i ch n ph ng pháp Join là NESTED LOOP ? t i sao CUSTOMERSế ể ạ ắ ắ ạ ạ ọ ươ ạ l i đ c ch n làm ạ ượ ọ outer-table ?, quy t đ nh ch n này đ c th c hi n b i Trình t i u câu l nh Oracle.ế ị ọ ượ ự ệ ở ố ư ệ
Trình t i u câu l nh Oracleố ư ệ
Trình t i u giúp Oracle xác đ nh đ c m t EP t t nh t cho m t câu l nh SQL. Trình t i u Oracle 9i h tr ph ngố ư ị ượ ộ ố ấ ộ ệ ố ư ỗ ợ ươ pháp t i u d a vào cú pháp l nh (Rule Based Optimizer – RBO) và ph ng pháp t i u d a vào chi phí c tính c nố ư ự ệ ươ ố ư ự ướ ầ dùng đ th c thi câu l nh (Cost Based Optimizer – CBO). M c đ nh Oracle9i dùng RBO.ể ự ệ ặ ị
RBO
RBO có t phiên b n Oracle6, d a vào c u trúc câu l nh SQL đ xác đ nh EP t t nh t. RBO s d ng m t l c đừ ả ự ấ ệ ể ị ố ấ ử ụ ộ ượ ồ phân h ng các ph ng th c truy c p d li u đ ch n ph ng th c truy c p cho EP, ph ng th c nào có s h ngạ ươ ứ ậ ữ ệ ể ọ ươ ứ ậ ươ ứ ố ạ th p s đ c u tiên ch n. L c đ phân h ng các ph ng th c truy c p d li u đ c qui đ nh nh sau:ấ ẽ ượ ư ọ ượ ồ ạ ươ ứ ậ ữ ệ ượ ị ư
Ta th y truy c p theo ROWID có s h ng th p nh t là 1; nghĩa là ph ng th c truy c p đ n d li u nhanh nh t, vàấ ậ ố ạ ấ ấ ươ ứ ậ ế ữ ệ ấ truy c p theo Index có s h ng là 4 s nhanh h n nhi u so v i truy c p theo Full-Table-Scan có s h ng cao nh t làậ ố ạ ẽ ơ ề ớ ậ ố ạ ấ 15.
Ví d sau cho th y RBO s d ng lu t phân h ng đ ch n ph ng th c truy c p d li u cho EP. Do c t CUST_ID c aụ ấ ử ụ ậ ạ ể ọ ươ ứ ậ ữ ệ ộ ủ đi u ki n WHERE có UNIQUE INDEX lên RBO ch n ph ng th c truy c p theo Index:ề ệ ọ ươ ứ ậ
SQL> DROP TABLE new_table;
Table dropped.
SQL> CREATE TABLE new_table AS
SELECT object_id, object_name, object_type
FROM all_objects
WHERE rownum <= 5000; 2 3 4
Table created.
SQL> SET AUTOTRACE TRACEONLY EXPLAIN
SQL> SELECT * FROM new_table WHERE object_type = 'TABLE';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'NEW_TABLE'
SQL> SET AUTOTRACE OFF
SQL> CREATE INDEX idx_object_type ON new_table(object_type);
Index created.
SQL> SET AUTOTRACE TRACEONLY EXPLAIN
SQL> SELECT * FROM new_table WHERE object_type = 'TABLE';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'NEW_TABLE'
2 1 INDEX (RANGE SCAN) OF 'IDX_OBJECT_TYPE' (NON-UNIQUE)
CBO
CBO có t phiên b n Oracle7, CBO d a vào “thông tin t ng h p” đ c đ c tính chi phí c a EP, EP t t nh t là EPừ ả ự ổ ợ ượ ể ướ ủ ố ấ có chi phí c tính nh nh t. Oracle9i ch dùng CBO đ t i u n u đ i t ng tham chi u đ n có thông tin mô t .ướ ỏ ấ ỉ ể ố ư ế ố ượ ế ế ả
“Thông tin t ng h p” trong ng c nh này bao g m:ổ ợ ữ ả ồ
1. Thông tin v h đi u hành máy ch Oracle: s CPU, t c đ đ c ghi đĩa c ng, kích th c kh i d li u, cề ệ ề ủ ố ố ộ ọ ứ ướ ố ữ ệ ơ ch qu n lý đĩa …ế ả
2. Thông tin mô t các đ i t ng d li u: s dòng, c t và kh i d li u c a Table, kích th c dòng, chi u caoả ố ượ ữ ệ ố ộ ố ữ ệ ủ ướ ề và s nút lá c a cây Index …ố ủ
3. Thông tin v các thông s liên quan: optimizer_mode, db_file_multiblock_read_count,ề ố parallel_automatic_tuning …
N u cung c p đ y đ và chính xác các thông tin trên, trình t i u CBO có th xác đ nh đ c m t EP t t nh t.ế ấ ầ ủ ố ư ể ị ượ ộ ố ấ
Ti p theo ví d trong ph n RBO, chúng ta dùng l nh ANALYZE TABLE đ thu th p thông tin c a NEW_TABLE cho trìnhế ụ ầ ệ ể ậ ủ t i u: ố ư
SQL> SET AUTOTRACE OFF
SQL> @li NEW_TABLE
indexes on table NEW_TABLE%:
TABLE_NAME INDEX_TYPE INDEX_NAME
-------------------- ---------- ------------------------------
NEW_TABLE NONUNIQUE IDX_OBJECT_TYPE
SQL> ANALYZE TABLE new_table COMPUTE STATISTICS;
Table analyzed.
SQL> SET AUTOTRACE TRACEONLY EXPLAIN
SQL>SELECT * FROM new_table WHERE object_type = 'TABLE';
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=5 Card=2500 Bytes=107500)
1 0 TABLE ACCESS (FULL) OF 'NEW_TABLE' (Cost=5 Card=2500 Bytes=107500)
Ta th y k t qu c a EP có m t s đi m khác bi t so v i th i đi m tr c khi thu th p thông tin:ấ ế ả ủ ộ ố ể ệ ớ ờ ể ướ ậ
1. Có thêm thông tin v chi phí th c hi n c a m i b c l nh: (Cost=5 Card=2500 Bytes=107500), đi u nàyề ự ệ ủ ỗ ướ ệ ề ch ng t Oracle hi n đang dùng trình t i u CBOứ ỏ ệ ố ư
2. CBO ch n ph ng th c truy c p TABLE ACCESS (FULL) m c dù có t n t i Index trên c t object_type.ọ ươ ứ ậ ặ ồ ạ ộ
Ph l c ụ ụ
Gi i nghĩa tả ừ
Bind-variable: k thu t vi t mã không gán giá tr c đ nh, k thu t này giúp tăng tính s n sàng, hi u năng và b oỹ ậ ế ị ố ị ỹ ậ ẵ ệ ả m t cho h th ng Oracle. Ví d : ậ ệ ố ụ
Mã không dùng Bind-Variable
BEGIN
UPDATE employees SET SALARY=SALARY + 100 WHERE employee_id = 100;
END;
Mã dùng Bind-Variable
DECLARE
bind_var NUMBER := 100;
BEGIN
UPDATE employees SET SALARY=SALARY + 100 WHERE employee_id = bind_var;
END;
Outer-table: t ng dòng d li u c a Table này s đ i sánh v i t t c dòng c a inner-table, còn g i là driving-table,ừ ữ ệ ủ ẽ ố ớ ấ ả ủ ọ đ c đ c p đ n trong ph ng pháp NESTED LOOP JOIN, th ng đi chung v i thu t ng inner-tableượ ề ậ ế ươ ườ ớ ậ ữ
Inner-table: t t c dòng d li u c a Table này s đ c đ i sánh v i t ng dòng c a outer-table. ấ ả ữ ệ ủ ẽ ượ ố ớ ừ ủ
Mã l nhệ
M t s File mã l nh ti n ích s d ng trong bài vi t: ộ ố ệ ệ ử ụ ế
Tên mã l nhệ Mô t ch c năngả ứ
li.sql
Li t kê t t c Index c a b ng nh p vàoệ ấ ả ủ ả ậ
select ui.table_name
, decode(ui.index_type
,'NORMAL', ui.uniqueness
,ui.index_type) as index_type
, ui.index_name
from user_indexes ui
where ui.table_name like upper('&1.%')
order by ui.table_name, ui.uniqueness desc
L ch s thay đ iị ử ổ
1. 10/02/2009 - Gi i thi u bài vi tớ ệ ế2. 20/02/2009 - B sung ph n "Công c t i u câu l nh: SQL*Plus AUTOTRACEổ ầ ụ ố ư ệ
Tham kh oả
1. Cách s d ng "Bind Variable" khi l p trình Java v i Oracleử ụ ậ ớ 2. Tài li u chu n c a Oracle Database 9iệ ẩ ủ 3. Tài li u khóa h c Oracle Database 9i: SQL Tuningệ ọ
M i các b n đ c ph n ti p theo c a ch đ này: ờ ạ ọ ầ ế ủ ủ ề
Ph n 2 – T i u câu l nh SQL có Table và có m t đi u ki nầ ố ư ệ ộ ề ệ
Ph n 2: T i u câu l nh SQL có m t Tableầ ố ư ệ ộ và có m t đi u ki nộ ề ệTi p theo ế Ph n 1: n n t ng c b n t i u câu l nh Oracle SQLầ ề ả ơ ả ố ư ệ ; gi i thi u các ki n th c c b n c n thi t đ t i uớ ệ ế ứ ơ ả ầ ế ể ố ư câu l nh SQL, chúng ta b t đ u đi vào tìm hi u k thu t t i u câu l nh c b n nh t, đó là t i u d a vào Index, cệ ắ ầ ể ỹ ậ ố ư ệ ơ ả ấ ố ư ự ụ th là B*tree-Index, lo i Index th ng đ c dùng nh t. ể ạ ườ ượ ấ
Chúng ta cũng c n l u ý là Index ch giúp tăng t c đ truy v n trong m t s tr ng h p c th , không ph i b t kìầ ư ỉ ố ộ ấ ộ ố ườ ợ ụ ể ả ấ tr ng h p nào s d ng Index cũng giúp tăng t c đ . Trong th c t , ta có th xem ph n m c l c c a m t cu nườ ợ ử ụ ố ộ ự ế ể ầ ụ ụ ủ ộ ố sách gi ng nh m t c u trúc Index; t m c l c cu n sách, b n suy ra đ c s th t trang sách ch a n i dung b nố ư ộ ấ ừ ụ ụ ố ạ ượ ố ứ ự ứ ộ ạ c n tìm. N u sách vài trăm trang b n có th d a vào m c l c đ tìm đ c n i dung mong mu n m t cách nhanhầ ế ạ ể ự ụ ụ ể ượ ộ ố ộ chóng, nh ng n u sách ch vài trang thì tìm theo cách l t t ng trang thì l i nhanh h n. Cách tìm theo m c l c gi ngư ế ỉ ậ ừ ạ ơ ụ ụ ố nh ph ng th c truy c p TABLE ACCESS (BY INDEX ROWID), cách tìm l t t ng trang sách gi ng nh ph ng th cư ươ ứ ậ ậ ừ ố ư ươ ứ truy c p TABLE ACCESS (FULL). ậ
Ph n hai này s t p trung ch y u vào k thu t t i u câu l nh s d ng Index, xem xét tr ng h p nào Oracle sầ ẽ ậ ủ ế ỹ ậ ố ư ệ ử ụ ườ ợ ẽ dùng Index trong câu l nh và ng c l i. Các ví d minh h a trong bài s dùng Table tên CUSTOMERS c a tài kho nệ ượ ạ ụ ọ ẽ ủ ả SH, c u trúc CUSTOMERS nh sau:ấ ư
[oracle@localhost LABS]$ sid
ORACLE_SID=ora9i
[oracle@localhost LABS]$
[oracle@localhost LABS]$ alias sh
alias sh='sqlplus "sh/sh"'
[oracle@localhost LABS]$ sh
SQL*Plus: Release 9.2.0.4.0 - Production on Mon Feb 16 08:56:20 2009
Copyright (c) 1982, 2002, Oracle Corporation. All rights reserved.
Connected to:
Oracle9i Enterprise Edition Release 9.2.0.4.0 - Production
With the Partitioning and Oracle Data Mining options
JServer Release 9.2.0.4.0 - Production
SQL> DESC customers
Name Null? Type
----------------------------------------------------------- -------- ---------------
CUST_ID NOT NULL NUMBER
CUST_FIRST_NAME NOT NULL VARCHAR2(20)
CUST_LAST_NAME NOT NULL VARCHAR2(40)
CUST_GENDER CHAR(1)
CUST_YEAR_OF_BIRTH NUMBER(4)
CUST_MARITAL_STATUS VARCHAR2(20)
CUST_STREET_ADDRESS NOT NULL VARCHAR2(40)
CUST_POSTAL_CODE NOT NULL VARCHAR2(10)
CUST_CITY NOT NULL VARCHAR2(30)
CUST_STATE_PROVINCE VARCHAR2(40)
COUNTRY_ID NOT NULL CHAR(2)
CUST_MAIN_PHONE_NUMBER VARCHAR2(25)
CUST_INCOME_LEVEL VARCHAR2(30)
CUST_CREDIT_LIMIT NUMBER
CUST_EMAIL VARCHAR2(30)
CUST_TOTAL VARCHAR2(14)
SQL>
Các tr ng h p ng d ng Index khi t i u câu l nh SQL ườ ợ ứ ụ ố ư ệ
Oracle không dùng Index cho đi u ki n so sánh có toán t ề ệ ử < >, != và NOT IN
Th ng chúng ta nghĩ n u c t d li u trong m nh đ đi u ki n có Index, thì câu l nh SQL s s d ng Index đ truyườ ế ộ ữ ệ ệ ề ề ệ ệ ẽ ử ụ ể v n d li u cho nhanh, tuy nhiên có nhi u tr ng h p dù có Index nh ng Index v n không đ c. Tr ng h p đ uấ ữ ệ ề ườ ợ ư ẫ ượ ườ ợ ầ tiên chúng ta xét đ n là đ i v i các toán t ế ố ớ ử <>, != và NOT IN.
Tr c tiên, ta ki m tra Table tên CUSTOMERS hi n đang có nh ng Index nào:ướ ể ệ ữ
SQL> @li CUSTOMERS
indexes on table CUSTOMERS%:
TABLE_NAME INDEX_TYPE INDEX_NAME
-------------------- ---------- ------------------------------
CUSTOMERS UNIQUE CUSTOMERS_PK
NONUNIQUE CUST_CREDIT_LIMIT_IDX
CUST_EMAIL_IDX
CUST_LAST_NAME_IDX
Dùng mã l nh ệ dai.sql xóa các nonprimary-key, khi đó CUSTOMERS ch còn ỉ primary-key tên CUSTOMERS_PK trên c t d li u CUST_ID:ộ ữ ệ
SQL> @dai
on which table: CUSTOMERS
DROP INDEX CUSTOMERS_PK
*
ERROR at line 1:
ORA-02429: cannot drop index used for enforcement of unique/primary key
SQL> @li CUSTOMERS
indexes on table CUSTOMERS%:
TABLE_NAME INDEX_TYPE INDEX_NAME
-------------------- ---------- ------------------------------
CUSTOMERS UNIQUE CUSTOMERS_PK
Xem cách trình t i u Oracle x lý b n câu l nh sau:ố ư ử ố ệ
SQL> SET AUTOTRACE TRACEONLY EXPLAIN
SQL> SELECT cust_first_name, cust_last_name
FROM customers
WHERE cust_id = 1030
/
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'CUSTOMERS'
2 1 INDEX (UNIQUE SCAN) OF 'CUSTOMERS_PK' (UNIQUE)
SQL> SELECT cust_first_name, cust_last_name
FROM customers
WHERE cust_id < 20000
/
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'CUSTOMERS'
2 1 INDEX (RANGE SCAN) OF 'CUSTOMERS_PK' (UNIQUE)
SQL> SELECT cust_first_name, cust_last_name
FROM customers
WHERE cust_id between 70000 and 80000
/
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'CUSTOMERS'
2 1 INDEX (RANGE SCAN) OF 'CUSTOMERS_PK' (UNIQUE)
SQL> SELECT cust_first_name, cust_last_name
FROM customers
WHERE cust_id <> 1030
/
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'CUSTOMERS'
SQL>
Nhìn vào k t qu các câu l nh, ta th y trình t i u Oracle đang đ c ch đ nh giá tr là Optimizer=CHOOSE, nghĩa làế ả ệ ấ ố ư ượ ỉ ị ị s ch n CBO n u CUSTOMERS có thông tin mô t và ch n RBO n u không có. M c đ nh CUSTOMERS không có thôngẽ ọ ế ả ọ ế ặ ị tin mô t , vì v y trong tr ng h p này trình t i u s dùng ph ng pháp RBO đ xác đ nh EP cho các câu l nh. ả ậ ườ ợ ố ư ẽ ươ ể ị ệ
Nh tìm hi u trong ph n tr c, RBO s d ng l c đ lu t phân h ng đ ch n EP t t nh t, u tiên ch n ph ngư ể ầ ướ ử ụ ượ ồ ậ ạ ể ọ ố ấ ư ọ ươ th c truy c p d li u theo Index h n là ph ng th c duy t t ng dòng d li u c a Table. Nh ng theo ví d trên, taứ ậ ữ ệ ơ ươ ứ ệ ừ ữ ệ ủ ư ụ th y RBO ch dùng Index cho 3 câu l nh đ u tiên, không dùng Index cho câu l nh cu i cùng. Đ ý thì ta th y ba câuấ ỉ ệ ầ ệ ố ể ấ l nh đ u s d ng toán t so sánh ệ ầ ử ụ ử =, < và BETWEEN AND, các toán t này đ u có khuynh h ng gi i h n t p dử ề ướ ớ ạ ậ ữ li u tr v , còn câu l nh th t thì s d ng toán t ệ ả ề ệ ứ ư ử ụ ử <>, toán t này luôn tr v t p k t qu l n. Nh v y, Oracleử ả ề ậ ế ả ớ ư ậ RBO ng x nh v y là h p lý, dùng Index cho câu l nh s d ng toán t so sánh có khuynh h ng tr v ít d li uứ ử ư ậ ợ ệ ử ụ ử ướ ả ề ữ ệ và không dùng Index cho tr ng h p câu l nh có toán t so sánh tr v nhi u d li u.ườ ợ ệ ử ả ề ề ữ ệ
RBO s ng x t ng t nh ẽ ứ ử ươ ự ư <> cho các toán t ử != và NOT IN.
Oracle không dùng Index cho c t d li u k t h p v i b t kì thành ph n khácộ ữ ệ ế ợ ớ ấ ầ
Dù c t d li u có Index, nh ng n u ta k t h p nó v i b t kì thành ph n nào khác, ch ng h n nh m t giá tr , m tộ ữ ệ ư ế ế ợ ớ ấ ầ ẳ ạ ư ộ ị ộ bi u th c thì Index trên c t d li u đó s không đ c trình t i u Oracle ng dùng. Xem xét các ví d d i đây:ể ứ ộ ữ ệ ẽ ượ ố ư ứ ụ ướ
SQL> SELECT *
2 FROM customers
3 WHERE cust_id + 1 = 100;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'CUSTOMERS'
SQL> SELECT *
2 FROM customers
3 WHERE TO_NUMBER(cust_id) = 100;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'CUSTOMERS'
SQL> SELECT *
2 FROM customers
3 WHERE cust_id + null = 100;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'CUSTOMERS'
SQL> SELECT *
2 FROM customers
3 WHERE cust_id = 100;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'CUSTOMERS'
2 1 INDEX (UNIQUE SCAN) OF 'CUSTOMERS_PK' (UNIQUE)
SQL>
Ta th y ba tr ng h p đ u tiên, dù c t d li u k t h p v i giá tr NULL , giá tr r ng, cũng khi n Oracle RBO khôngấ ườ ợ ầ ộ ữ ệ ế ợ ớ ị ị ỗ ế ng d ng Index cho câu l nh. Đ i v i tr ng h p TO_NUMBER(cust_id) thì do hàm TO_NUMBER k t bu c v i c tứ ụ ệ ố ớ ườ ợ ế ộ ớ ộ
CUST_ID nên làm m t tác d ng c a Index, tuy nhiên chúng ta có th s d ng k thu t ấ ụ ủ ể ử ụ ỹ ậ function-based Index; t oạ Index tr c ti p trên hàm k t bu c vào c t d li u, ự ế ế ộ ộ ữ ệ đ th c hi n ph ng th c truy c p d li u theo Index mà khôngể ự ệ ươ ứ ậ ữ ệ c n b hàm TO_NUMBER().ầ ỏ
SQL> CREATE INDEX cust_id_tonumber_idx ON customers(to_number(cust_id));
Index created.
SQL> @li
indexes on table CUSTOMERS%:
TABLE_NAME INDEX_TYPE INDEX_NAME
-------------------- ---------- ------------------------------
CUSTOMERS UNIQUE CUSTOMERS_PK
NONUNIQUE CUST_CREDIT_LIMIT_IDX
FUNCTION-B CUST_ID_TONUMBER_IDX
ASED NORMA
L
SQL> SET AUTOTRACE TRACEONLY EXPLAIN
SQL> SELECT *
2 FROM customers
3 WHERE to_number(cust_id) = 100;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'CUSTOMERS'
SQL> ANALYZE TABLE customers COMPUTE STATISTICS;
Table analyzed.
SQL> SELECT *
2 FROM customers
3 WHERE to_number(cust_id) = 100;
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=137)
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'CUSTOMERS' (Cost=2 Card=1 Bytes=137)
2 1 INDEX (RANGE SCAN) OF 'CUST_ID_TONUMBER_IDX' (NON-UNIQUE) (Cost=1 Card=1)
SQL>
Nh ta th y, ch trình t i u CBO m i hi u k thu t Function-based Index, RBO không hi u. Vì v y, đ trình t i uư ấ ỉ ố ư ớ ể ỹ ậ ể ậ ể ố ư Oracle t đ ng dùng CBO thì chúng ta c n thu th p thông tin mô t cho CUSTOMERS.ự ộ ầ ậ ả
L u ý thêm v cách thu th p thông tin mô t cho Table, chúng ta s d ng cú pháp l nh ANALYZE TABLE vì l nh nàyư ề ậ ả ử ụ ệ ệ đ n gi n, d hi u và cú pháp trong sáng, đáp ng đ cho các minh h a c a chúng ta. Khi ng d ng thu th p thôngơ ả ễ ể ứ ủ ọ ủ ứ ụ ậ tin cho h th ng Oracle th c t , các b n nên dùng gói l nhệ ố ự ế ạ ệ DBMS_STATS thì hi u qu h n.ệ ả ơ
Oracle Index và toán t LIKEử
Đ minh h a cho ý này, chúng ta s t o thêm m t Index trên c t d li u CUST_LAST_NAME theo mã l nh ể ọ ẽ ạ ộ ộ ữ ệ ệ ci.sql:
SQL> @ci
on which table : CUSTOMERS
on which column(s): cust_last_name
Creating index on: CUSTOMERS cust_last_name
Enter value for index_name: cust_last_name_idx
SQL> @li CUSTOMERS
indexes on table CUSTOMERS%:
TABLE_NAME INDEX_TYPE INDEX_NAME
-------------------- ---------- ------------------------------
CUSTOMERS UNIQUE CUSTOMERS_PK
NONUNIQUE CUST_CREDIT_LIMIT_IDX
FUNCTION-B CUST_ID_TONUMBER_IDX
ASED NORMA
L
NONUNIQUE CUST_LAST_NAME_IDX
Ti p theo, chúng ta xét ví d sau:ế ụ
SQL> SET AUTOTRACE TRACEONLY EXPLAIN
SQL> SELECT cust_id
2 FROM customers
3 WHERE cust_last_name LIKE 'S%'
/
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (BY INDEX ROWID) OF 'CUSTOMERS'
2 1 INDEX (RANGE SCAN) OF 'CUST_LAST_NAME_IDX' (NON-UNIQUE)
SQL> SELECT cust_last_name
2 FROM customers
3 WHERE cust_last_name LIKE '%S%’
/
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'CUSTOMERS'
SQL> SELECT cust_id
2 FROM customers
3 WHERE cust_last_name LIKE '%S'
/
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'CUSTOMERS'
SQL>
Nh v y, trình t i u Oracle RBO ch ng d ng Index cho toán t LIKE n u giá tr so sánh không có kí t đ c bi t ư ậ ố ư ỉ ứ ụ ử ế ị ự ặ ệ % đ u. ở ầ
Chúng ta xét ti p ví d sau:ế ụ
SQL> SELECT cust_last_name
2 FROM customers
3 WHERE cust_last_name like 'S%'
/
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 INDEX (RANGE SCAN) OF 'CUST_LAST_NAME_IDX' (NON-UNIQUE)
SQL>
Các b n th y đi m khác bi t đây ch ? EP không có b c l nh TABLE ACCESS (BY INDEX ROWID) nh bìnhạ ấ ể ệ ở ứ ướ ệ ư th ng, mà ch có INDEX (RANGE SCAN). ườ ỉ
Nguyên nhân do c t d li u k t qu tr v ch có CUST_LAST_NAME, mà d li u này chính là giá tr khóa c a Indexộ ữ ệ ế ả ả ề ỉ ữ ệ ị ủ tên CUST_LAST_NAME_IDX, vì v y Oracle ch c n duy t cây Index là có th l y đ c k t qu mong mu n, không c nậ ỉ ầ ệ ể ấ ượ ế ả ố ầ t n thêm chi phí duy t Table nh bình th ng.ố ệ ư ườ
Ti p t c v i ví d sau:ế ụ ớ ụ
SQL> SELECT cust_last_name
2 FROM customers
3 WHERE cust_id LIKE '7%'
/
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'CUSTOMERS'
SQL>
Các b n có đoán ra đ c lý do t i sao trình t i u Oracle RBO không ng dùng Index trên c t d li u CUST_ID ? Doạ ượ ạ ố ư ứ ộ ữ ệ CUST_ID là c t d li u ki u s , nh ng câu l nh l i so sánh v i m t giá tr chu i ‘7%’, nên trong tr ng h p nàyộ ữ ệ ể ố ư ệ ạ ớ ộ ị ỗ ườ ợ Oracle t đ ng ng m đ nh chuy n đ i ki u cho m nh đ đi u ki n thành nh sau:ự ộ ầ ị ể ổ ể ệ ề ề ệ ư
SQL> SELECT cust_last_name
2 FROM customers
3 WHERE TO_CHAR(cust_id) LIKE '7%'
Do c t d li u CUST_ID b k t bu c v i hàm TO_CHAR() khi so sánh, nên b m t tác d ng Index.ộ ữ ệ ị ế ộ ớ ị ấ ụ
Tóm t t nh ng ý chúng ta đã tìm hi u đ c trong ph n này:ắ ữ ể ượ ầ
1. Oracle ch ng d ng Index cho toán t LIKE khi giá tr so sánh không có kí t ỉ ứ ụ ử ị ự % đ u ở ầ2. N u k t qu d li u c a câu l nh có th tìm th y đ trong Index, thì Oracle ch c n duy t cây Index đ l yế ế ả ữ ệ ủ ệ ể ấ ủ ỉ ầ ệ ể ấ
k t qu tr v , mà không c n duy t trên Table.ế ả ả ề ầ ệ3. N u c t d li u trong m nh đ đi u ki n so sánh v i m t giá tr khác ki u, Oracle t đ ng chuy n đ i ki uế ộ ữ ệ ệ ề ể ệ ớ ộ ị ể ự ộ ể ổ ể
ng m đ nh cho c t d li u đó, đi u này khi n Index không đ c ng d ng.ầ ị ộ ữ ệ ề ế ượ ứ ụ
Oracle Index và giá tr NULLị
Theo ki n trúc c s d li u Oracle, c u trúc cây Index ,B*tree-Index, không l u thông tin v dòng d li u c a Tableế ơ ở ữ ệ ấ ư ề ữ ệ ủ có giá tr khóa là NULL. V y theo b n, Oracle s ng x th nào khi so sánh đi u ki n trên c t d li u có giá trị ậ ạ ẽ ứ ử ế ề ệ ộ ữ ệ ị NULL ? chúng ta s tìm hi u ý này ngay trong ph n d i đây.ẽ ể ầ ướ
Đ chu n b cho ph n này, chúng ta c p nh t m t s giá tr c a c t d li u CUST_EMAIL v NULL và t o Index tênể ẩ ị ầ ậ ậ ộ ố ị ủ ộ ữ ệ ề ạ CUST_EMAIL_IDX trên c t này.ộ
SQL> SET AUTOTRACE OFF
SQL> UPDATE customers
2 SET cust_email = null
3 WHERE rownum < 101
/
100 rows updated.
SQL> COMMIT;
Commit complete.
SQL> @ci
on which table : CUSTOMERS
on which column(s): CUST_EMAIL
Creating index on: CUSTOMERS CUST_EMAIL
Enter value for index_name: CUST_EMAIL_IDX
SQL> SET AUTOTRACE TRACEONLY EXPLAIN
SQL> SELECT cust_email
2 FROM customers
3 WHERE cust_email IS NULL
/
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'CUSTOMERS'
SQL>
Theo ví d trên, Oracle đã không dùng Index dù c t CUST_EMAIL có Index. Gi i thích cho cách ng x này là doụ ộ ả ứ ử B*tree-Index không l u thông tin v dòng d li u ch a giá tr khóa là NULL, vì v y Oracle ph i th c hi n quét t ngư ề ữ ệ ứ ị ậ ả ự ệ ừ dòng d li u c a Table đ tìm đ c các CUST_EMAIL có giá tr NULL.ữ ệ ủ ể ượ ị
V y các b n th xem ti p ví d sau, t i sao Oracle v n không dùng Index ?ậ ạ ử ế ụ ạ ẫ
SQL> SET AUTOTRACE TRACEONLY EXPLAIN
SQL> SELECT cust_id
2 FROM customers
3* WHERE cust_email IS NOT NULL
SQL> /
Execution Plan
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE
1 0 TABLE ACCESS (FULL) OF 'CUSTOMERS'
SQL>
N u Oracle B*tree-Index không l u giá tr NULL, v y khi tìm giá tr khác NULL thì theo suy lu n bình th ng Oracleế ư ị ậ ị ậ ườ ph i dùng Index trong tr ng h p này ? Không nh v y, Oracle “nghĩ” r ng đi u ki n IS NOT NULL s tr v t p k tả ườ ợ ư ậ ằ ề ệ ẽ ả ề ậ ế qu l n, n u dùng Index s không hi u qu , nên Oracle ch n ph ng th c truy c p d li u TABLE ACCESS (FULL) sả ớ ế ẽ ệ ả ọ ươ ứ ậ ữ ệ ẽ hi u qu h n.ệ ả ơ
Ph l c ụ ụ
Mã l nhệ
M t s File mã l nh ti n ích s d ng trong bài vi t: ộ ố ệ ệ ử ụ ế
Tên mã l nhệ Mô t ch c năngả ứ
li.sql
Li t kê t t c Index c a b ng nh p vàoệ ấ ả ủ ả ậ
select ui.table_name, decode(ui.index_type ,'NORMAL', ui.uniqueness ,ui.index_type) as index_type, ui.index_namefrom user_indexes uiwhere ui.table_name like upper('&1.%')order by ui.table_name, ui.uniqueness desc
dai.sql
Xóa h t t t c non-primary key Index trên m t c t d li u nh p vào c a m t Tableế ấ ả ộ ộ ữ ệ ậ ủ ộ
accept TABLE_NAME prompt " on which table: "set termout offstore set saved_settings replaceset heading off verify off autotrace off feedback off
spool doit.sql
select 'DROP INDEX '||ui.index_name||';'from user_indexes uiwhere table_name like upper('&TABLE_NAME.%')/
spool offset termout on@doit@saved_settingsundef TABLE_NAMEset termout on
ci.sqlT o m i m t Non-Unique Index trên m t c t d li u nh p vào c a m t Tableạ ớ ộ ộ ộ ữ ệ ậ ủ ộ
accept TABLE_NAME prompt " on which table : "
|accept COLUMN_NAME prompt " on which column(s): "
set termout offstore set saved_settings replaceset heading off feedback off autotrace offset verify off termout on
select 'Creating index on: ', '&&TABLE_NAME', '&&COLUMN_NAME'FROM DUAL/
create index &INDEX_NAME on &TABLE_NAME(&COLUMN_NAME)/
@saved_settingsset termout onundef INDEX_NAMEundef TABLE_NAMEundef COLUMN_NAME