一个SQL COUNT 优化案例,作一个小结
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的执行计划是不是还有那么优越,就要视情况而定了!
最后,这确实是一种优化的好思路!
分享到:
相关推荐
在缺省情况下,ORACLE采用CHOOSE优化器, 为了避免那些不必要的全表扫描(full table scan) , 你必须尽量避免使用CHOOSE优化器,而直接采用基于规则或者基于成本的优化器. 2. 访问Table的方式 ORACLE 采用两种...
In a nonclustered index, the leaf level contains each index key, plus a bookmark that tells SQL Server where to find the data row corresponding to the key in the index. A bookmark can take one of two ...
Index An entire index of a table. Table An entire table, including all data and indexes. Extent A contiguous group of data pages or index pages. Page An 8-KB data page or index page. Key Row lock ...
书名: SQLServer2008查询性能优化 作者: 弗里奇(Grant Fritchey) 出版社: 人民邮电出版社 出版日期: 2010年8月1日 ISBN: 9787115230294 编辑推荐 《SQL Server 2008查询性能优化》为你提供了处理查询性能所...
Before we look at how SQL Server uses and manages its memory, we need to ensure a full understanding of the more common memory related terms. The following definitions will help you understand how SQL...
书名: SQLServer2008查询性能优化 作者: 弗里奇(Grant Fritchey) 出版社: 人民邮电出版社 出版日期: 2010年8月1日 ISBN: 9787115230294 编辑推荐 《SQL Server 2008查询性能优化》为你提供了处理查询性能所...
--使用IF UPDATE(column)尽可能优化上题的触发器,以提高系统效率 ALTER TRIGGER UPDATE_ProNo_Stocks_TRIGGER ON Product FOR UPDATE AS IF UPDATE (ProNo) PRINT '已更新' GO --单元十三 游标 --创建存储过程...
第17 天使用SQL 来生成SQL 语句351 目标351 使用SQL 来生成SQL 语句的目的351 几个SQL*PLUS 命令352 SET ECHO ON/OFF353 SET FEEDBACK ON/OFF353 SET HEADING ON/OFF 353 SPOOL FILENAME/OFF353 START FILENAME354 ...
12.2.2 db_file_multiblock_read_count 328 12.2.3 pga_aggregate_target和sga_target 334 12.2.4 optimizer_dynamic_sampling 334 第13章 性能报告 335 13.1 awr性能报告 335 13.1.1 生成awr性能报告 337 13.1.2 ...
1 SQL基础 1.1 基本概念 结构化查询语言(Structured Query Language)简称SQL,是一种关系数据库查询语言,用于存取数据以及查询、更新和管理关系数据库系统。 1.2 语句结构 1.2.1 数据查询语言(DQL) 对数据库进行...
12.2.2 db_file_multiblock_read_count 328 12.2.3 pga_aggregate_target和sga_target 334 12.2.4 optimizer_dynamic_sampling 334 第13章 性能报告 335 13.1 awr性能报告 335 13.1.1 生成awr性能报告 337 13.1.2 ...
§6.2.9 步骤9:优化资源争用 89 §6.2.10 步骤10:优化所采用的平台 89 §6.3 应用优化方法 90 §6.3.1 设定明确的优化目标 90 §6.3.2 创建最少可重复测试 90 §6.3.3 测试假想 90 §6.3.4 记录和自动测试 90 §...
10.2 SQL优化 257 10.2.1 测试执行计划 257 10.2.2 跨多个执行的测试 260 10.2.3 测试查询改变的影响 263 10.2.4 寻找其他优化机会 266 10.2.5 将子查询因子化应用到PL/SQL中 270 10.3 递归子查询 273 ...
♦ 进阶 SQL: 介绍 SQL 进阶概念,以及如何用 SQL 来执行一些较复杂的运算。 ♦ SQL 语法: 这一页列出所有在这个教材中被提到的 SQL 语法。 对于每一个指令, 我们将会先列出及解释这个指令的语法, 然后用一个例子...
一般采用二级索引去count:比如:id ...但是如果用secondary index 代码如下:select count(*) from table where aid>=0; 则会快很多。 为什么用 secondary index 扫描反而比 primary key 扫描来的要快呢?这就需要了解inn
), interpreted (然后 PL/SQL 模块将被编译为 PL/SQL 字节代码格式), debug (PL/SQL 模块将用探测调试符号来编译), non_debug。 默认值: " interpreted, non_debug " plsql_native_linker: 说明: 此参数指定链接...
SQL>select index_name,index_type,table_name from user_indexes order by table_name; 查看索引被索引的字段 SQL>select * from user_ind_columns where index_name=upper('&index_name'); 查看索引的大小 ...
§1.10 使用SQL Worksheet工作 46 第二章 查询基础 50 §2.1 SELECT语句 50 §2.2 SQL中的单记录函数 50 §2.2.1 单记录字符函数 50 §2.2.2 单记录数字函数 56 §2.2.3 单记录日期函数 62 §2.2.4 单记录转换函数 ...