分区表无法使用分区索引,请教

[复制链接]
查看11 | 回复9 | 2010-3-1 11:08:24 | 显示全部楼层 |阅读模式
各位好。现在遇到一个问题描述如下:
有一个分区表,数据大概总共大概是1500万左右,按表中的字段RQ按月分区,条件例 values less than to_date('2006-02-01 00:00:00','yyyy-mm-dd hh24:mi:ss),每月分区大概有个60多万数据,建了一个RQ字段 的分区索引,以及一个(字段a,rq,字段B)的分区索引,表已做了完全分析。现在我执行类似
select * from tablename where to_char(rq,'yyyymmd')='20060801';这样的语句时,速度超慢,察看了一下SQL_TRACE的结果
call count cpuelapsed diskquerycurrentrows
------- -------------- ---------- ---------- ---------- --------------------
Parse10.01 0.01
0
0
0 0
Execute10.00 0.00
0
0
0 0
Fetch1 40.3191.7211728941173098
0 0
------- -------------- ---------- ---------- ---------- --------------------
total3 40.3291.7311728941173098
0 0
Misses in library cache during parse: 1
Optimizer mode: ALL_ROWS
Parsing user id: 54
Rows Row Source Operation
----------------------------------------------------------
0PARTITION RANGE ALL PARTITION: 1 23 (cr=0 pr=0 pw=0 time=14 us)
0 TABLE ACCESS FULLTABLENAMEPARTITION: 1 23 (cr=1163010 pr=1162799 p
w=0 time=90932866 us)
语句没有到相应分区中找,而是遍历了所有分区。
请问:
1、这是为什么,是索引建的不合理么,怎么样才能使他体现
分区的优势
2、用了rq字段作为分区,是不是不需要再建单独的rq索引,或者说rq索引是多余的了
回复

使用道具 举报

千问 | 2010-3-1 11:08:24 | 显示全部楼层
因为你的条件用了to_char()函数
回复

使用道具 举报

千问 | 2010-3-1 11:08:24 | 显示全部楼层
最初由 油菜 发布
[B]因为你的条件用了to_char()函数 [/B]

老大,这个是什么概念,你能详细说一下吗。那要用怎么样的改动一下呢
回复

使用道具 举报

千问 | 2010-3-1 11:08:24 | 显示全部楼层
发生了隐式转换
导致索引无效
从而是所有的分区扫描
看看tomszrp 的那篇文章的
前几天还有呢
回复

使用道具 举报

千问 | 2010-3-1 11:08:24 | 显示全部楼层
最初由 duolanshizhe 发布
[B]发生了隐式转换
导致索引无效
从而是所有的分区扫描
看看tomszrp 的那篇文章的
前几天还有呢 [/B]

我找不到他的文章,点搜索就"无法显示网页了".
能不能有什么釜底抽薪的方法,因为不能要求每个用这个数据库的人写的SQL语句都是符合能调用索引的规范的。能不能通过重建表,调整分区条件、建索引等其他方法解决呢?
回复

使用道具 举报

千问 | 2010-3-1 11:08:24 | 显示全部楼层
一:你建的应该是range分区。没有用到分区修剪,可能和你的sql语句有关(不能确认)
二:where条件中用了to_char函数,无法使用索引,此外,你用的select * from ,还要看你取得的行数有多少,行数太多,优化器会判断出全表扫表比索引更快的。
回复

使用道具 举报

千问 | 2010-3-1 11:08:24 | 显示全部楼层
那我给你贴出来
[php]
关于字符和数字类型的索引,Oracle如何实现内部自动转换以及索引使用的验证测试
发表人:tomszrp | 发表时间: 2006年二月10日, 18:51
起因:今天早上有同事问我如下语句为什么执行很慢:
select * from tbcs.acc_bank_debit_info a
where a.region=534 and
a.status=1 and
a.accountid=(select accountoid from tbcs.subscriber b
where b.status='US10' and
b.region=534 and
b.servnumber=13626373466);
备注:以上2表均是按照region进行分区的分区表,在accountid上都有索引.

根据以前的经验,我一看就知道是子查询未用上索引的缘故,因为servernumber是varchar类型的,而... where b.status='US10' and b.region=534 and b.servnumber=13626373466这种写法是用不上索引的。
修改为... where b.status='US10' and b.region=534 and b.servnumber='13626373466'后,性能马上得以解决。
为了验证我对这个观点的认识,就顺手做了个实验,结果和我期望的一致。
实验环境:
windows xp2 + Oracle9i + 760M
Oracle9i Enterprise Edition Release 9.2.0.1.0 - Production
PL/SQL Release 9.2.0.1.0 - Production
CORE 9.2.0.1.0 Production
TNS for 32-bit Windows: Version 9.2.0.1.0 - Production
NLSRTL Version 9.2.0.1.0 - Production
验证过程如下:
SQL>create table zrp (
recoid number(14),
servnumber varchar2(9),
region number(3),
deal_date date ) tablespace study;
--编写脚本插入数据(多弄点感受一下效果)
SQL> begin
2 for i in 1..99999 loop
3 insert into zrp values(to_number('53109053'||to_char(i,'fm00000')),'8053'||to_char(i,'fm00000'),531,sysdate);
4 end loop;
5 commit;
6 end;
7 /
PL/SQL 过程已成功完成。
SQL>
SQL> begin
2 for i in 1..99999 loop
3 insert into zrp values(to_number('53208053'||to_char(i,'fm00000')),'8053'||to_char(i,'fm00000'),532,sysdate);
4 end loop;
5 commit;
6 end;
7 /
PL/SQL 过程已成功完成。
SQL>
...
SQL> select count(*) from zrp;
299997
SQL>
--分别在number类型和varchar2类型的字段上recoid和servnumber上建立索引
SQL> create index inx_zrp_oid on zrp(recoid);
索引已创建。
SQL> create index inx_zrp_serv on zrp (servnumber);
索引已创建。
SQL>
--下面开始测试验证过程,先验证number类型字段上的索引是否能对字符串进行自动转换,从而使用上索引.
select * from zrp where recoid=5310905310005
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.00 0.03 3 6 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.00 0.03 3 6 0 1
Misses in library cache during parse: 1
Optimizer mode: CHOOSE
Parsing user id: 59
Rows Row Source Operation
------- ---------------------------------------------------
1 TABLE ACCESS BY INDEX ROWID ZRP
1 INDEX RANGE SCAN INX_ZRP_OID (object id 30594)
********************************************************************************
select * from zrp where recoid='5310905310080'
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.00 0.00 2 6 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.00 0.00 2 6 0 1
Misses in library cache during parse: 1
Optimizer mode: CHOOSE
Parsing user id: 59
Rows Row Source Operation
------- ---------------------------------------------------
1 TABLE ACCESS BY INDEX ROWID ZRP
1 INDEX RANGE SCAN INX_ZRP_OID (object id 30594)
********************************************************************************
/*
结果证明:对于number类型的字段的索引,Oracle在内部能实现对字符串和number之间的自动转换,从而继续使用索引.下面在看看varchar2类型字段上的索引是否能对数字进行自动转换,从而使用上索引呢
*/
select * from zrp where servnumber='905310001'
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.00 0.02 2 6 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.00 0.03 2 6 0 1
Misses in library cache during parse: 1
Optimizer mode: CHOOSE
Parsing user id: 59
Rows Row Source Operation
------- ---------------------------------------------------
1 TABLE ACCESS BY INDEX ROWID ZRP
1 INDEX RANGE SCAN INX_ZRP_SERV (object id 30595)
********************************************************************************
select * from zrp where servnumber=905318899
call count cpu elapsed disk query current rows
------- ------ -------- ---------- ---------- ---------- ---------- ----------
Parse 1 0.00 0.00 0 0 0 0
Execute 1 0.00 0.00 0 0 0 0
Fetch 2 0.35 0.45 799 1513 0 1
------- ------ -------- ---------- ---------- ---------- ---------- ----------
total 4 0.35 0.45 799 1513 0 1
Misses in library cache during parse: 1
Optimizer mode: CHOOSE
Parsing user id: 59
Rows Row Source Operation
------- ---------------------------------------------------
1 TABLE ACCESS FULL ZRP
********************************************************************************
哈哈,看到了,太明显了。此时对于select * from zrp where servnumber=905318899 语句就用不上servnumber字段上的索引了.
分析总结:
当比较不同数据类型的数据时, ORACLE自动对列进行简单的类型转换. 上面recoid是一个数值类型的索引列.
select * from zrp where recoid='5310905310080';
实际上,经过ORACLE类型转换, 语句转化为:
select * from zrp where recoid=to_number('5310905310080');
类型转换没有发生在索引列上,索引的用途没有被改变. 现在,而servnumber是一个字符类型的索引列.
select * from zrp where servnumber=905318899
这个语句被ORACLE转换为:
select * frorm zrp to_number(servnumber)=905318899;
因为内部发生的类型转换, 这个索引将不会被用到!
为什么前者不不是发生在列上而后者是呢,闲着没事将Oracle内部对varchar/char和number类型的数据结构做了一个dump,发现引起这个内部转换可能与数据的存储类型有关系。
因为dump的过程比较罗嗦,也不容易看懂,此处略去,引用别人的总结如下:
>

最后,我又翻阅了Oracle官方文档,找到了如下的说法,证实了上面的结论:
...
The following rules govern the direction in which Oracle makes implicit datatype conversions:
1) During INSERT and UPDATE operations, Oracle converts the value to the datatype of the affected column.
2) During SELECT FROM operations, Oracle converts the data from the column to the type of the target variable.
3) When comparing a character value with a NUMBER value, Oracle converts the character data to NUMBER.
4) When comparing a character value with a DATE value, Oracle converts thecharacter data to DATE.
5) When you use a SQL function or operator with an argument of a datatype other than the one it accepts,
Oracle converts the argument to the accepted datatype.
6) When making assignments, Oracle converts the value on the right side of the equal sign (=) to the datatype of the target of the assignment on the left side.
7) During concatenation operations, Oracle converts from noncharacter datatypes to CHAR or NCHAR.
8) During arithmetic operations on and comparisons between character and noncharacter datatypes,
Oracle converts from any character datatype to a number, date, or rowid, as appropriate.
In arithmetic operations between CHAR/VARCHAR2 and NCHAR/NVARCHAR2, Oracle converts to a number.
9) Comparisons between CHAR/VARCHAR2 and NCHAR/NVARCHAR2 types may entail different character sets.
The default direction of conversion in such cases is from the database character set to the national character set.

以上9个要点尤为重要,其他的可以不看,这9个声明一定要看.这一步骤,也再次证明了阅读Oracle官方文档的重要性.

[/php]
回复

使用道具 举报

千问 | 2010-3-1 11:08:24 | 显示全部楼层
to_char(rq,'yyyymmd')='20060801'
改成
rq=to_date('20060801','yyyymmd')
回复

使用道具 举报

千问 | 2010-3-1 11:08:24 | 显示全部楼层
最初由 marchyc 发布
[B]
因为不能要求每个用这个数据库的人写的SQL语句都是符合能调用索引的规范的。 [/B]

不能要求?
这是必须要求的!!!!
回复

使用道具 举报

千问 | 2010-3-1 11:08:24 | 显示全部楼层
非常感谢duolanshizhe。我先学习一下。
最初由 alantany 发布
[B]to_char(rq,'yyyymmd')='20060801'
改成
rq=to_date('20060801','yyyymmd') [/B]

我的数据格式是yyyy-mm-dd hh24:mi:ss的,这样用to_date就找不到数据了,要么就用
>=to_date('20060801 00:00:00','yyyymmdd hh24:mi:ss') and <=to_date('20060801 23:59:59','yyyymmdd hh24:mi:ss')这样了
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

主题

0

回帖

4882万

积分

论坛元老

Rank: 8Rank: 8

积分
48824836
热门排行