[20131025]一條sql語句的優化.txt
[20131025]一條sql語句的優化.txt
最近在優化一條sql語句,做一個測試例子測試看看。遇到一些問題記錄一下:
1.建立環境:
create table t (id1 number,id2 number,name varchar2(100) ) pctfree 99;
alter table t modify(id1 not null);
alter table t modify(id2 not null);
insert into t select rownum id,1e4-rownum+1 id2 ,lpad('x',100,'x') from dual connect by level <=1e4;
commit ;
--這樣建立了一張大表,記錄10000行.注意這張表id1,id2沒有等於0的記錄。
create index i_t_id1 on t(id1);
create index i_t_id2 on t(id2);
exec dbms_stats.gather_table_stats(user,'T',cascade=>TRUE);
2.測試:
--注意:生產環境cursor_sharing=force。
--模擬語句如下:程式沒有使用繫結變數,直接帶數值的。
select * from t where (id1=440 or 440=0) and (id2=0 or 0=0);
select * from t where (id1=0 or 0=0) and (id2=9561 or 9561=0);
select * from t where (id1=440 or 440=0) and (id2=9561 or 9561=0);
--如果使用繫結變數,語句如下:
select * from t where (id1=:v1 or :v1=0) and (id2=:v2 or :v2=0);
-- 可以看出開發的本意是程式既可以查詢id1欄位也可以查詢id2欄位,一個很牛逼的設計!
-- 初始帶入的引數都是0。
3.在cursor_sharing=exact的情況下:
--可以發現cursor_sharing=exact的情況下,oracle知道0=0為真9561=0為假,這樣 (id1=0 or 0=0) and (id2=9561 or 9561=0)
--實際變為id2=9561,這樣的情況下可以使用i_t_id2索引。
--看看執行計劃2 - access("ID2"=9561)就很容易明白。
4.在cursor_sharing=force的情況下:
--注意我換了1個值,440=>430.原來的語句還是會使用索引。
SCOTT@test> @dpc '' ''
--換成這樣,oracle就瞎眼了,不知道里面的內在邏輯,僅僅選擇全表掃描。
使用提示呢?
--這樣做INDEX FULL SCAN 掃描,效率不如全表掃描。
5.解決方法:
1.修改引數cursor_sharing=exact,這樣可以徹底解決這樣,實際上這樣的類似的語句有許多處。
--補充1點,我個人認為如果現在開發的OLTP系統再沒有使用並且合理的使用繫結變數,這樣的專案可以講就是一個豆腐渣工程!
--oracle相關優化的書籍對這個問題多次提到。不要簡單以為修改引數cursor_sharing=force可以解決這個問題。
2.當然是修改程式程式碼。程式中不要出現or,並且改成繫結變數。
3.實際上還有一種方法就是加入提示 /*+ CURSOR_SHARING_EXACT */ ,這樣就不會轉換裡面的常量數值,從而解決這個問題。
最近在優化一條sql語句,做一個測試例子測試看看。遇到一些問題記錄一下:
1.建立環境:
SCOTT@test> @ver
BANNER
--------------------------------------------------------------------------------
Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Production
create table t (id1 number,id2 number,name varchar2(100) ) pctfree 99;
alter table t modify(id1 not null);
alter table t modify(id2 not null);
insert into t select rownum id,1e4-rownum+1 id2 ,lpad('x',100,'x') from dual connect by level <=1e4;
commit ;
--這樣建立了一張大表,記錄10000行.注意這張表id1,id2沒有等於0的記錄。
create index i_t_id1 on t(id1);
create index i_t_id2 on t(id2);
exec dbms_stats.gather_table_stats(user,'T',cascade=>TRUE);
2.測試:
--注意:生產環境cursor_sharing=force。
--模擬語句如下:程式沒有使用繫結變數,直接帶數值的。
select * from t where (id1=440 or 440=0) and (id2=0 or 0=0);
select * from t where (id1=0 or 0=0) and (id2=9561 or 9561=0);
select * from t where (id1=440 or 440=0) and (id2=9561 or 9561=0);
--如果使用繫結變數,語句如下:
select * from t where (id1=:v1 or :v1=0) and (id2=:v2 or :v2=0);
-- 可以看出開發的本意是程式既可以查詢id1欄位也可以查詢id2欄位,一個很牛逼的設計!
-- 初始帶入的引數都是0。
3.在cursor_sharing=exact的情況下:
SCOTT@test> select * from t where (id1=440 or 440=0) and (id2=0 or 0=0);
ID1 ID2 NAME
---------- ---------- --------------------
440 9561 xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID 627f4u46pmh2a, child number 0
-------------------------------------
select * from t where (id1=440 or 440=0) and (id2=0 or 0=0)
Plan hash value: 1183254286
---------------------------------------------------------------------
| Id | Operation | Name | E-Rows | Cost (%CPU)|
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | 2 (100)|
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 2 (0)|
|* 2 | INDEX RANGE SCAN | I_T_ID1 | 1 | 1 (0)|
---------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID1"=440)
SCOTT@test> select * from t where (id1=0 or 0=0) and (id2=9561 or 9561=0);
ID1 ID2 NAME
---------- ---------- --------------------
440 9561 xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID avvjxv0a4dvs5, child number 0
-------------------------------------
select * from t where (id1=0 or 0=0) and (id2=9561 or 9561=0)
Plan hash value: 3974417878
---------------------------------------------------------------------
| Id | Operation | Name | E-Rows | Cost (%CPU)|
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | 2 (100)|
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 2 (0)|
|* 2 | INDEX RANGE SCAN | I_T_ID2 | 1 | 1 (0)|
---------------------------------------------------------------------
Predicate Information (identified by operation id):
--------------------------------------------------
2 - access("ID2"=9561)
--可以發現cursor_sharing=exact的情況下,oracle知道0=0為真9561=0為假,這樣 (id1=0 or 0=0) and (id2=9561 or 9561=0)
--實際變為id2=9561,這樣的情況下可以使用i_t_id2索引。
--看看執行計劃2 - access("ID2"=9561)就很容易明白。
4.在cursor_sharing=force的情況下:
SCOTT@test> select * from t where (id1=430 or 430=0) and (id2=0 or 0=0);
ID1 ID2 NAME
---------- ---------- --------------------
430 9571 xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
--注意我換了1個值,440=>430.原來的語句還是會使用索引。
SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
--------------------------------------------------------------------------
SQL_ID 5ff9cdq6bmvgt, child number 1
-------------------------------------
select * from t where (id1=:"SYS_B_0" or :"SYS_B_1"=:"SYS_B_2") and
(id2=:"SYS_B_3" or :"SYS_B_4"=:"SYS_B_5")
Plan hash value: 1601196873
--------------------------------------------------------
| Id | Operation | Name | E-Rows | Cost (%CPU)|
--------------------------------------------------------
| 0 | SELECT STATEMENT | | | 2740 (100)|
|* 1 | TABLE ACCESS FULL| T | 1 | 2740 (1)|
--------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(((:SYS_B_1=:SYS_B_2 OR "ID1"=:SYS_B_0) AND
(:SYS_B_4=:SYS_B_5 OR "ID2"=:SYS_B_3)))
--換成這樣,oracle就瞎眼了,不知道里面的內在邏輯,僅僅選擇全表掃描。
使用提示呢?
SCOTT@test> select /*+ index(t i_t_id1) */ * from t where (id1=430 or 430=0) and (id2=0 or 0=0);
ID1 ID2 NAME
---------- ---------- --------------------
430 9571 xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------
SQL_ID 18buqaphf9pzt, child number 0
-------------------------------------
select /*+ index(t i_t_id1) */ * from t where (id1=:"SYS_B_0" or
:"SYS_B_1"=:"SYS_B_2") and (id2=:"SYS_B_3" or :"SYS_B_4"=:"SYS_B_5")
Plan hash value: 2057376682
---------------------------------------------------------------------
| Id | Operation | Name | E-Rows | Cost (%CPU)|
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | 10026 (100)|
|* 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 10026 (1)|
| 2 | INDEX FULL SCAN | I_T_ID1 | 10000 | 22 (0)|
---------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter(((:SYS_B_1=:SYS_B_2 OR "ID1"=:SYS_B_0) AND
(:SYS_B_4=:SYS_B_5 OR "ID2"=:SYS_B_3)))
--這樣做INDEX FULL SCAN 掃描,效率不如全表掃描。
5.解決方法:
1.修改引數cursor_sharing=exact,這樣可以徹底解決這樣,實際上這樣的類似的語句有許多處。
--補充1點,我個人認為如果現在開發的OLTP系統再沒有使用並且合理的使用繫結變數,這樣的專案可以講就是一個豆腐渣工程!
--oracle相關優化的書籍對這個問題多次提到。不要簡單以為修改引數cursor_sharing=force可以解決這個問題。
2.當然是修改程式程式碼。程式中不要出現or,並且改成繫結變數。
3.實際上還有一種方法就是加入提示 /*+ CURSOR_SHARING_EXACT */ ,這樣就不會轉換裡面的常量數值,從而解決這個問題。
SCOTT@test> show parameter cursor_sharing
NAME TYPE VALUE
------------------------------------ ---------------------------------------- -----------------------
cursor_sharing string FORCE
SCOTT@test> select /*+ CURSOR_SHARING_EXACT */ * from t where (id1=450 or 450=0) and (id2=0 or 0=0);
ID1 ID2 NAME
---------- ---------- --------------------
450 9551 xxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxx
SCOTT@test> @dpc '' ''
PLAN_TABLE_OUTPUT
-------------------------------------
SQL_ID 058w6jcj7twzr, child number 0
-------------------------------------
select /*+ CURSOR_SHARING_EXACT */ * from t where (id1=450 or 450=0)
and (id2=0 or 0=0)
Plan hash value: 1183254286
---------------------------------------------------------------------
| Id | Operation | Name | E-Rows | Cost (%CPU)|
---------------------------------------------------------------------
| 0 | SELECT STATEMENT | | | 2 (100)|
| 1 | TABLE ACCESS BY INDEX ROWID| T | 1 | 2 (0)|
|* 2 | INDEX RANGE SCAN | I_T_ID1 | 1 | 1 (0)|
---------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("ID1"=450)
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/267265/viewspace-775123/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 一條sql語句的優化SQL優化
- 一條SQL語句的優化過程SQL優化
- [20181114]一條sql語句的優化.txtSQL優化
- [20151203]一條sql語句的優化.txtSQL優化
- [20150715]一條sql語句的優化.txtSQL優化
- [20120319]一條sql語句的優化.txtSQL優化
- [20130319]一條sql語句的優化.txtSQL優化
- [20151209]一條sql語句的優化(續).txtSQL優化
- [20140210]一條sql語句的優化(11g).txtSQL優化
- SQL語句優化--十條經驗SQL優化
- 一條sql語句優化不出來, 哭了一鼻子SQL優化
- 一條update語句的優化探索優化
- 一個SQL語句的優化SQL優化
- SQL語句的優化SQL優化
- 一條sql語句的建議調優分析SQL
- 一條簡單的SQL語句優化-新年新氣象SQL優化
- SQL語句優化SQL優化
- 總結出10條SQL語句優化精髓SQL優化
- SQL Server優化之SQL語句優化SQLServer優化
- SQL 語句的優化方法SQL優化
- 一次sql語句優化的反思SQL優化
- Mysql 52條SQL語句效能優化策略彙總MySql優化
- 通過_optimizer_rownum_pred_based_fkr優化一條sql語句優化SQL
- [20131204]sql語句優化.txtSQL優化
- [20151221]sql語句優化.txtSQL優化
- MYSQL SQL語句優化MySql優化
- sql語句效能優化SQL優化
- 求助:SQL語句優化SQL優化
- [20170104]一條sql優化.txtSQL優化
- 一條SQL語句的書寫SQL
- 一條很 巧妙的 SQL 語句SQL
- 一條SQL語句的旅行之路SQL
- SQL語句優化的34條建議(轉-浪花七八朵 )SQL優化
- 優化 SQL 語句的步驟優化SQL
- 關於sql語句的優化SQL優化
- sql語句的優化案例分析SQL優化
- [20201210]sql語句優化.txtSQL優化
- MySQL之SQL語句優化MySql優化