SQL最佳化-COUNT_ INDEX的巧用
一個SQL COUNT 最佳化案例,作一個小結[@more@]
SQL需求:在一個8000W的表中,找出修改時間為某一時間段的記錄,並且某欄位不為空:
select count(*)
from aliim.udb_user_profile a
where a.gmt_modified >= to_date('2008-07-21 14:29:38', 'yyyy-mm-dd hh24:mi:ss')
and a.gmt_modified <= to_date('2008-07-21 15:34:50', 'yyyy-mm-dd hh24:mi:ss')
and a.AEP_USER_ID is not null
首先想到的是在 gmt_modified 欄位中建立單列索引,讓查詢走索引,並返回到表中過濾 a.AEP_USER_ID is not null.
create index udb_user_profile_gmtm_ind on udb_user_profile(gmt_modified ) online compute statistics ;
在測試庫,看執行計劃:
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------------
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 75 | 6 |
| 1 | SORT AGGREGATE | | 1 | 75 | |
|* 2 | TABLE ACCESS BY INDEX ROWID| UDB_USER_PROFILE | 481 | 36075 | 6 |
|* 3 | INDEX RANGE SCAN | UDB_USER_PROFILE_GMTM_IND | 17308 | | 2 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("A"."AEP_USER_ID" IS NOT NULL)
3 - access("A"."GMT_MODIFIED">=TO_DATE('2008-07-20 14:29:38', 'yyyy-mm-dd hh24:mi:ss')
AND "A"."GMT_MODIFIED"<=TO_DATE('2008-07-21 15:34:50', 'yyyy-mm-dd hh24:mi:ss'))
Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
27 consistent gets
18 physical reads
0 redo size
379 bytes sent via SQL*Net to client
500 bytes received via SQL*Net from client
2 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
1 rows processed
結果比較理想,但到了產品庫中,相同的執行計劃,卻發現這條SQL執行了40多分鐘.......
原因是因為生產庫中所能選擇到的資料遠比測試庫要多得多(10W以上).
我們作了分析:
oracle先走索引:UDB_USER_PROFILE_GMTM_IND ,找到滿足條件的ROWID後(如果是100000條,至少是300個塊),那麼下一步就去表中找到這100000條記錄(最壞的情況是這100000記錄都分散在不同的塊,那麼這一次的讀將會是100000個),最後作filter("A"."AEP_USER_ID" IS NOT NULL)並返回實際滿足條件的COUNT值。
初想這個執行計劃沒有啥大問題,而且也走得似乎很合情合理,但就是時間太長了,,,,,
下面是高手出馬的結果:
從 GMT_MODIFIED 列來看,我們沒有更好的辦法來最佳化,那麼我們來看一下AEP_USER_ID這個列中有沒有文章可做:
我們來看一下AEP_USER_ID的資料分佈:
-----------------
NOT NULL : 5000000
NULL : 75000000
我們知道,ORACLE的索引是不儲存為NULL的資料的,因為NOTNULL的資料比較少,所以索引也相對比較小。 如果讓COUNT不訪問表而直接訪問索引,那將會是一個比較理想的路徑,於是就大膽的作了測試:
create index udb_user_profile_auid_ind on udb_user_profile(AEP_USER_ID) online compute statistics;
再將SQL改成如下形式:
select /*+ordered use_hash(a b) index_ffs(b UDB_USER_PROFILE_AUID_IND)*/
count(*)
from aliim.udb_user_profile a, aliim.udb_user_profile b
where a.rowid = b.rowid
and a.gmt_modified >=
to_date('2008-07-20 14:29:38', 'yyyy-mm-dd hh24:mi:ss')
and a.gmt_modified <=
to_date('2008-07-21 15:34:50', 'yyyy-mm-dd hh24:mi:ss')
and b.AEP_USER_ID is not null
看執行計劃:
-------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost |
-------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 22 | 7 |
| 1 | SORT AGGREGATE | | 1 | 22 | |
|* 2 | HASH JOIN | | 1 | 22 | 7 |
|* 3 | INDEX RANGE SCAN | UDB_USER_PROFILE_GMTM_IND | 2 | 28 | 4 | (直接走index即可)
|* 4 | INDEX FAST FULL SCAN| UDB_USER_PROFILE_AUID_IND | 1 | 8 | 2 |
-------------------------------------------------------------------------------------
oracle只走了兩個索引,將對兩個索引作了HASH連線,沒有回表。
當完全配置後,執行這個SQL只用了不到1分鐘的時間。
最佳化過程中,主要原理還是讓ORACLE去訪問更少的資料塊。
當然這種方法還是有一定的風險:
當b.AEP_USER_ID 的非空值越來越大時,那麼訪問UDB_USER_PROFILE_AUID_IND這個索引的時間也會越來越長,到時候這個SQL的執行計劃是不是還有那麼優越,就要視情況而定了!
最後,這確實是一種最佳化的好思路!
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/703656/viewspace-1007653/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- no_index最佳化sql一例IndexSQL
- MySQL SQL最佳化 - 覆蓋索引(covering index)MySql索引Index
- SQL*PLUS -L選項巧用SQL
- 高效的SQL(index values與index column values關係?)SQLIndex
- 探究Presto SQL引擎(1)-巧用AntlrRESTSQL
- 【SQL最佳化】SQL最佳化的10點注意事項SQL
- 【SQL 提示 之二】index_ss Index Skip HintSQLIndex
- sql最佳化:使用sql profile最佳化sql語句SQL
- 巧用rowid簡化sql查詢SQL
- 巧用連線請求和調查SQL Server的提高SQLServer
- SQL的最佳化[轉]SQL
- oracle INDEX BY Pl/sql陣列OracleIndexSQL陣列
- 收集full table / index scan sqlIndexSQL
- 【SQL優化】LIKE vs INDEXSQL優化Index
- INDEX建立方式對SQL的影響IndexSQL
- 高效的SQL(Index unique scan最優化)SQLIndex優化
- 高效的SQL(index range scan優化排序)SQLIndex優化排序
- 高效的SQL(index skip scan使用條件)SQLIndex
- 巧用SQL Server(Ranking)實現view的排序功能SQLServerView排序
- SQL最佳化SQL
- 最佳化sql的利器SQLTSQL
- Oracle SQL的最佳化[轉]OracleSQL
- Oracle SQL Like 的最佳化OracleSQL
- sql語句的最佳化SQL
- 巧用JDBC連線SQL SERVER 2008JDBCSQLServer
- 巧用SET選項提高SQL Server客戶端的靈活性SQLServer客戶端
- SQL最佳化1SQL
- PL/SQL最佳化SQL
- sql最佳化(mysql)MySql
- SQL最佳化方案SQL
- sql最佳化技巧SQL
- 最佳化SQL Server索引的技巧SQLServer索引
- 基於Oracle的SQL最佳化OracleSQL
- sql最佳化的幾種方式SQL
- mysql 索引巧用,SQL語句寫得忒野了MySql索引
- 巧用ROW_NUMBER 實現SQL資料任意排序SQL排序
- 【最佳化】INDEX FULL SCAN (MIN/MAX)訪問路徑Index
- 基於Oracle的sql最佳化(1)OracleSQL