SQL優化之把運算放在列的另一端
今天幫朋友解決oracle程式異常中斷後不能登入的問題後,順便幫他看了一下造成IDLE幾乎為0的原因。
通過分析發現,都是因為不良SQL造成的。這些不良SQL的一個共同點就是:把運算放在了列端。
朋友的資料量非常大,我在本機上簡單模擬現場,資料量上會小很多,但是仍然可以不同的寫法對執行效率的差別。
1、準備資料
SQL> create table test(name varchar2(100),code number(6),birth date);
Table created
--這個表中,code表示地區編碼,前兩位表示所屬省份;birth表示出生日期
SQL> insert into test select 'suk',110000,trunc(sysdate-30000)+rownum from dual connect by rownum<30000;
29999 rows inserted
SQL> insert into test values('suk',120000,trunc(sysdate-10000));
1 row inserted
SQL> commit;
Commit complete
SQL> create index idx_test_name_code on test(name,code);
Index created
SQL> create index idx_test_name_birth on test(name,birth);
Index created
SQL> analyze table test compute statistics for table for all indexes for all indexed columns;
Table analyzed
2、查詢
在本文中,舉兩個例子:
SQL1)查詢在12這個省中與'suk'同名的人的個數
SELECT COUNT(1) FROM TEST WHERE SUBSTR(CODE,1,2)=12 AND NAME='suk';
SQL2)查詢名字叫'suk'且出生日期是'1980-03-11'的人的個數
SELECT COUNT(1) FROM TEST WHERE NAME='suk' AND TO_CHAR(BIRTH,'YYYY-MM-DD')='1980-03-11';
這兩個SQL有一個共同點:就是把運算都放在了列上:SUBSTR(CODE,1,2)、TO_CHAR(BIRTH,'YYYY-MM-DD')。
我們在(name,code),(name,birth)都建立的組合索引,這兩個查詢都直接可以從查詢得到結果,而不用在回表查詢了。
但是,CODE的資料類似的NUMBER,那麼,規矩oracle資料類似轉換的規則可知,在實際執行的時候,SUBSTR(CODE,1,2)=12會轉換成:
TO_NUMBER(SUBSTR(TO_CHAR(CODE),1,2))=12。
在SQL1中,根據索引,oracle很快可以定位到name='suk'的葉塊(在本例中佔了絕大部分葉塊),但因為對CODE進行了各種的函式轉換,oracle無法快速確定SUBSTR(CODE,1,2)=12所在也塊,它需要在name='suk'得到的葉塊上一一排查,再結合根據資料分佈情況分析,SQL1的查詢計劃應該是全索引快速掃描。
我們可以檢視一下執行計劃:
suk@ORACLE9I> SELECT COUNT(1) FROM TEST WHERE SUBSTR(CODE,1,2)=12 AND NAME='suk';
COUNT(1)
----------
1
已用時間: 00: 00: 00.17
執行計劃
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=4 Card=1 Bytes=5)
1 0 SORT (AGGREGATE)
2 1 INDEX (FAST FULL SCAN) OF 'IDX_TEST_NAME_CODE' (NON-UNIQ
UE) (Cost=4 Card=300 Bytes=1500)
統計資訊
----------------------------------------------------------
0 recursive calls
0 db block gets
81 consistent gets
要調整這個SQL就要讓條件中出現的索引欄位不經過任何轉換,這樣就可以快速定位記錄了。SQL1可以等價改寫為如下:
suk@ORACLE9I> SELECT COUNT(1) FROM TEST WHERE CODE>=120000 AND CODE<130000 AND NAME='suk';
COUNT(1)
----------
1
已用時間: 00: 00: 00.12
執行計劃
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=2 Card=1 Bytes=5)
1 0 SORT (AGGREGATE)
2 1 INDEX (RANGE SCAN) OF 'IDX_TEST_NAME_CODE' (NON-UNIQUE)
(Cost=2 Card=1 Bytes=5)
統計資訊
----------------------------------------------------------
0 recursive calls
0 db block gets
2 consistent gets
同理可以調整SQL2:
--調整前
suk@ORACLE9I> SELECT COUNT(1) FROM TEST WHERE NAME='suk' AND TO_CHAR(BIRTH,'YYYY-MM-DD')='1980-03-11
';
COUNT(1)
----------
2
已用時間: 00: 00: 00.17
執行計劃
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=5 Card=1 Bytes=12)
1 0 SORT (AGGREGATE)
2 1 INDEX (FAST FULL SCAN) OF 'IDX_TEST_NAME_BIRTH' (NON-UNI
QUE) (Cost=5 Card=300 Bytes=3600)
統計資訊
----------------------------------------------------------
0 recursive calls
0 db block gets
104 consistent gets
--調整後
suk@ORACLE9I> SELECT COUNT(1) FROM TEST WHERE NAME='suk' AND BIRTH=TO_DATE('1980-03-11','YYYY-MM-DD'
);
COUNT(1)
----------
2
已用時間: 00: 00: 00.14
執行計劃
----------------------------------------------------------
0 SELECT STATEMENT Optimizer=CHOOSE (Cost=1 Card=1 Bytes=12)
1 0 SORT (AGGREGATE)
2 1 INDEX (RANGE SCAN) OF 'IDX_TEST_NAME_BIRTH' (NON-UNIQUE)
(Cost=1 Card=300 Bytes=3600)
統計資訊
----------------------------------------------------------
0 recursive calls
0 db block gets
2 consistent gets
可以看到,調整後的SQL用索引範圍掃描代替了全索引快速掃描,邏輯讀大大降低。
3、簡單總結
SQL調優的一個原則就是不要在條件中出現的列上做任何操作(包括不要加函式、不要發生資料隱式轉換等),我們要把操作儘量放在常量端。
換一句話說就是:建立索引時索引列是怎麼定義的,在where條件中列也要按相同的方式寫。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/231499/viewspace-63840/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Android效能優化之運算篇Android優化
- 高效的SQL(Function-based Indexes 函式、運算優化思路)SQLFunctionIndex函式優化
- SQL Server優化之SQL語句優化SQLServer優化
- sql優化之多列索引的使用SQL優化索引
- sql優化之邏輯優化SQL優化
- MySQL 效能優化之SQL優化MySql優化
- 前端效能優化---將script標籤放在body之後前端優化
- 陣列進行奇數和偶數操作 把奇數放在陣列前面 偶數放在陣列後面陣列
- SQL優化中索引列使用函式之靈異事件SQL優化索引函式事件
- SQL Server SQL效能優化之引數化SQLServer優化
- oracle優化一例之sql優化Oracle優化SQL
- MySQL之SQL優化技巧MySql優化
- SQL優化之limit 1SQL優化MIT
- 深度學習運算元優化-FFT深度學習優化FFT
- 陣列的位移運算陣列
- 【SQL】sql優化小工具之SQLHCSQL優化
- 行轉列計算差值的一種優化優化
- MySQL之SQL語句優化MySql優化
- Oracle之sql語句優化OracleSQL優化
- SQL位運算SQL
- Sql優化(二) 快速計算Distinct CountSQL優化
- 神奇的 SQL 之效能優化 → 讓 SQL 飛起來SQL優化
- Android效能優化篇之計算效能優化Android優化
- 資料庫效能優化之SQL語句優化資料庫優化SQL
- Python科學計算之Numpy陣列生成與運算Python陣列
- MySQL之SQL優化詳解(一)MySql優化
- MySQL之SQL優化詳解(二)MySql優化
- MySQL之SQL優化詳解(三)MySql優化
- 十七、Mysql之SQL優化查詢MySql優化
- MySQL SQL 優化之覆蓋索引MySql優化索引
- SQL優化之六脈神劍SQL優化
- Oracle SQL語句優化之UNIONOracleSQL優化
- Oracle優化之sql基本功Oracle優化SQL
- 【SQL優化】SQL優化的10點注意事項SQL優化
- Oracle SQL優化之sql tuning advisorOracleSQL優化
- Java 字串操作、基本運算方法等優化策略Java字串優化
- 《MySQL慢查詢優化》之SQL語句及索引優化MySql優化索引
- win10怎麼把軟體放在桌面 win10如何把app圖示放在桌面Win10APP