涉及子查詢sql的一次優化
前幾天對生產庫上的一個sql進行優化,語句結構如下
select min(s.create_time) from justin s
where exists(select 1 from justin_item si
where s.id = si.stat_id and s.status in(4,38)
and si.num_id = :1)
其目前執行計劃為
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 27 | | 52499 (1)| 00:10:30 |
| 1 | SORT AGGREGATE | | 1 | 27 | | | |
|* 2 | HASH JOIN | | 221K| 5852K| 4768K| 52499 (1)| 00:10:30 |
| 3 | TABLE ACCESS BY INDEX ROWID| justin_ITEM | 221K| 2165K| | 18610 (1)| 00:03:44 |
|* 4 | INDEX RANGE SCAN | IDX_SI_num_id | 226K| | | 983 (1)| 00:00:12 |
|* 5 | TABLE ACCESS FULL | justin | 261K| 4342K| | 33288 (1)| 00:06:40 |
------------------------------------------------------------------------------------------------------------
對錶justin選擇了全表掃描,而該表有幾百萬條記錄,因此效率十分低下;
剛開始新增hint
select /*+ index(s,pk_justin) */ min(s.create_time) from justin s
where exists(select 1 from justin_item si
where s.id = si.stat_id and s.status in(4,38)
and si.num_id = :a)
而此時的執行計劃如下
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 27 | | 310K (1)| 01:02:05 |
| 1 | SORT AGGREGATE | | 1 | 27 | | | |
|* 2 | HASH JOIN | | 221K| 5852K| 4768K| 310K (1)| 01:02:05 |
| 3 | TABLE ACCESS BY INDEX ROWID| justin_ITEM | 221K| 2165K| | 18610 (1)| 00:03:44 |
|* 4 | INDEX RANGE SCAN | IDX_SI_num_id | 226K| | | 983 (1)| 00:00:12 |
|* 5 | TABLE ACCESS BY INDEX ROWID| justin | 261K| 4342K| | 291K (1)| 00:58:14 |
| 6 | INDEX FULL SCAN | pk_justin | 1700K| | | 20941 (1)| 00:04:12 |
------------------------------------------------------------------------------------------------------------
但是consistent gets卻比新增前高了2倍
仔細觀察一下,對錶justin雖然選擇了主鍵掃描,但是卻是index full scan,且還要回表操作,代價比full table scan還要高;
兩表使用了hash join作為關聯,這意味著需要單獨取出兩表的候選資料,然後進行hash關聯;
而新增Hint的初衷是想要sql先訪問justin_item上的資料,然後將過濾出的資料按照s.id = si.stat_id的關聯條件去訪問justin表上的id主鍵,這就需要使用nest loop join;
再新增一個hint use_nl(si,s),再次檢視執行計劃
SQL> select /*+ use_nl(si,s) index(s,pk_justin) */ min(s.create_time) from justin s
2 where exists(select 1 from justin_item si
3 where s.id = si.stat_id and s.status in(4,38)
4 and si.num_id = :a)
5 ;
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 27 | | 310K (1)| 01:02:05 |
| 1 | SORT AGGREGATE | | 1 | 27 | | | |
|* 2 | HASH JOIN | | 221K| 5852K| 4768K| 310K (1)| 01:02:05 |
| 3 | TABLE ACCESS BY INDEX ROWID| justin_ITEM | 221K| 2165K| | 18610 (1)| 00:03:44 |
|* 4 | INDEX RANGE SCAN | IDX_SI_num_id | 226K| | | 983 (1)| 00:00:12 |
|* 5 | TABLE ACCESS BY INDEX ROWID| justin | 261K| 4342K| | 291K (1)| 00:58:14 |
| 6 | INDEX FULL SCAN | pk_justin | 1700K| | | 20941 (1)| 00:04:12 |
------------------------------------------------------------------------------------------------------------
發現執行計劃較上次並沒有改變,兩表關聯依舊為hash join
請注意,子查詢中有一行為s.status in (4, 38),此過濾條件根本不訪問justin_item,不需要放在子查詢中,將其移到外部,再次檢視執行計劃;
此時訪問pk_justin時用到了index unique scan,而邏輯讀由原來的404518下降為現在的2625,提升了200多倍。
SQL> select /*+ use_nl(si s) index(s pk_justin) */
2 min(s.create_time)
3 from justin s
4 where exists (select 1
5 from justin_item si
6 where s.id = si.stat_id
7 and si.num_id = :a)
8 and s.status in (4, 38);
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 27 | 241K (1)| 00:48:19 |
| 1 | SORT AGGREGATE | | 1 | 27 | | |
| 2 | NESTED LOOPS | | 221K| 5852K| 241K (1)| 00:48:19 |
| 3 | SORT UNIQUE | | 221K| 2165K| 18610 (1)| 00:03:44 |
| 4 | TABLE ACCESS BY INDEX ROWID| justin_ITEM | 221K| 2165K| 18610 (1)| 00:03:44 |
|* 5 | INDEX RANGE SCAN | IDX_SI_num_id | 226K| | 983 (1)| 00:00:12 |
|* 6 | TABLE ACCESS BY INDEX ROWID | justin | 1 | 17 | 2 (0)| 00:00:01 |
|* 7 | INDEX UNIQUE SCAN | pk_justin | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
select min(s.create_time) from justin s
where exists(select 1 from justin_item si
where s.id = si.stat_id and s.status in(4,38)
and si.num_id = :1)
其目前執行計劃為
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 27 | | 52499 (1)| 00:10:30 |
| 1 | SORT AGGREGATE | | 1 | 27 | | | |
|* 2 | HASH JOIN | | 221K| 5852K| 4768K| 52499 (1)| 00:10:30 |
| 3 | TABLE ACCESS BY INDEX ROWID| justin_ITEM | 221K| 2165K| | 18610 (1)| 00:03:44 |
|* 4 | INDEX RANGE SCAN | IDX_SI_num_id | 226K| | | 983 (1)| 00:00:12 |
|* 5 | TABLE ACCESS FULL | justin | 261K| 4342K| | 33288 (1)| 00:06:40 |
------------------------------------------------------------------------------------------------------------
對錶justin選擇了全表掃描,而該表有幾百萬條記錄,因此效率十分低下;
剛開始新增hint
select /*+ index(s,pk_justin) */ min(s.create_time) from justin s
where exists(select 1 from justin_item si
where s.id = si.stat_id and s.status in(4,38)
and si.num_id = :a)
而此時的執行計劃如下
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 27 | | 310K (1)| 01:02:05 |
| 1 | SORT AGGREGATE | | 1 | 27 | | | |
|* 2 | HASH JOIN | | 221K| 5852K| 4768K| 310K (1)| 01:02:05 |
| 3 | TABLE ACCESS BY INDEX ROWID| justin_ITEM | 221K| 2165K| | 18610 (1)| 00:03:44 |
|* 4 | INDEX RANGE SCAN | IDX_SI_num_id | 226K| | | 983 (1)| 00:00:12 |
|* 5 | TABLE ACCESS BY INDEX ROWID| justin | 261K| 4342K| | 291K (1)| 00:58:14 |
| 6 | INDEX FULL SCAN | pk_justin | 1700K| | | 20941 (1)| 00:04:12 |
------------------------------------------------------------------------------------------------------------
但是consistent gets卻比新增前高了2倍
仔細觀察一下,對錶justin雖然選擇了主鍵掃描,但是卻是index full scan,且還要回表操作,代價比full table scan還要高;
兩表使用了hash join作為關聯,這意味著需要單獨取出兩表的候選資料,然後進行hash關聯;
而新增Hint的初衷是想要sql先訪問justin_item上的資料,然後將過濾出的資料按照s.id = si.stat_id的關聯條件去訪問justin表上的id主鍵,這就需要使用nest loop join;
再新增一個hint use_nl(si,s),再次檢視執行計劃
SQL> select /*+ use_nl(si,s) index(s,pk_justin) */ min(s.create_time) from justin s
2 where exists(select 1 from justin_item si
3 where s.id = si.stat_id and s.status in(4,38)
4 and si.num_id = :a)
5 ;
------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 27 | | 310K (1)| 01:02:05 |
| 1 | SORT AGGREGATE | | 1 | 27 | | | |
|* 2 | HASH JOIN | | 221K| 5852K| 4768K| 310K (1)| 01:02:05 |
| 3 | TABLE ACCESS BY INDEX ROWID| justin_ITEM | 221K| 2165K| | 18610 (1)| 00:03:44 |
|* 4 | INDEX RANGE SCAN | IDX_SI_num_id | 226K| | | 983 (1)| 00:00:12 |
|* 5 | TABLE ACCESS BY INDEX ROWID| justin | 261K| 4342K| | 291K (1)| 00:58:14 |
| 6 | INDEX FULL SCAN | pk_justin | 1700K| | | 20941 (1)| 00:04:12 |
------------------------------------------------------------------------------------------------------------
發現執行計劃較上次並沒有改變,兩表關聯依舊為hash join
請注意,子查詢中有一行為s.status in (4, 38),此過濾條件根本不訪問justin_item,不需要放在子查詢中,將其移到外部,再次檢視執行計劃;
此時訪問pk_justin時用到了index unique scan,而邏輯讀由原來的404518下降為現在的2625,提升了200多倍。
SQL> select /*+ use_nl(si s) index(s pk_justin) */
2 min(s.create_time)
3 from justin s
4 where exists (select 1
5 from justin_item si
6 where s.id = si.stat_id
7 and si.num_id = :a)
8 and s.status in (4, 38);
-----------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 27 | 241K (1)| 00:48:19 |
| 1 | SORT AGGREGATE | | 1 | 27 | | |
| 2 | NESTED LOOPS | | 221K| 5852K| 241K (1)| 00:48:19 |
| 3 | SORT UNIQUE | | 221K| 2165K| 18610 (1)| 00:03:44 |
| 4 | TABLE ACCESS BY INDEX ROWID| justin_ITEM | 221K| 2165K| 18610 (1)| 00:03:44 |
|* 5 | INDEX RANGE SCAN | IDX_SI_num_id | 226K| | 983 (1)| 00:00:12 |
|* 6 | TABLE ACCESS BY INDEX ROWID | justin | 1 | 17 | 2 (0)| 00:00:01 |
|* 7 | INDEX UNIQUE SCAN | pk_justin | 1 | | 1 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------------
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/15480802/viewspace-703356/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 一文終結SQL 子查詢優化SQL優化
- 優化sql查詢速度優化SQL
- SQL查詢的:子查詢和多表查詢SQL
- exists與in子查詢優化優化
- 十七、Mysql之SQL優化查詢MySql優化
- Sql語句本身的優化-定位慢查詢SQL優化
- Mysql優化系列之——優化器對子查詢的處理MySql優化
- 《MySQL慢查詢優化》之SQL語句及索引優化MySql優化索引
- SQL優化之多表關聯查詢-案例一SQL優化
- PostgreSQL 原始碼解讀(29)- 查詢語句#14(查詢優化-上拉子查詢)SQL原始碼優化
- 查詢優化優化
- MySQL 的查詢優化MySql優化
- 記一次 Golang 資料庫查詢元件的優化。Golang資料庫元件優化
- MySQL exists關聯子查詢SQL效能及其低下最佳化之等值子查詢轉換MySql
- MySQL 優化五(關聯查詢子查詢以及 in 的效率問題)(高階篇)MySql優化
- 如何調優 Oracle SQL系列文章:查詢優化器介紹OracleSQL優化
- SQL語言基礎(子查詢)SQL
- sql-server不相關子查詢SQLServer
- sql-server相關子查詢SQLServer
- MySql常用30種SQL查詢語句優化方法MySql優化
- pgsql查詢優化之模糊查詢SQL優化
- 優化-mysql子查詢索引失效問題解決優化MySql索引
- Sql Server 的引數化查詢SQLServer
- Oracle in 查詢優化Oracle優化
- MySQL查詢優化MySql優化
- join 查詢優化優化
- HBase查詢優化優化
- 什麼是SQL 語句中相關子查詢與非相關子查詢SQL
- ORACLE_OCP之SQL_子查詢OracleSQL
- SQL連線查詢優化[姊妹篇.第五彈]SQL優化
- 關於樹結構的查詢優化,及許可權樹的查詢優化優化
- StoneDB 子查詢最佳化
- mysql查詢優化檢查 explainMySql優化AI
- KunlunDB 查詢優化(一)優化
- 分頁查詢優化優化
- MySQL 慢查詢優化MySql優化
- MySQL優化COUNT()查詢MySql優化
- EntityFramework優化:查詢WITH(NOLOCK)Framework優化
- EntityFramework優化:查詢效能Framework優化