我有两个包含以下列的表:
Table1
{ ID NUMBER(15),
ROLL_NUM VARCHAR2(9),
BATCH_NUM VARCHAR2(6),
ACCT_BALANCE NUMBER(15,2)
}
Table2
{ Table1_ID NUMBER(15) REFERENCES TABLE1.ID,
SEQ_NUM NUMBER(2),
TRANS_NUM VARCHAR2(10),
TRANS_AMT NUMBER(8,2),
TRANS_DT DATE
}
Table1有200,000条记录,Table2有500,000条记录
我有简单的连接如下:
SQL#1:
SELECT A.ROLL_NUM, A.ACCT_BALANCE, B.TRANS_NUM, TRANS_AMT, TRANS_DT
FROM TABLE1 A, TABLE2 B
WHERE B.Table1_ID = A.ID
AND A.BATCH_NUM = 400012
SQL#2:
SELECT A.ROLL_NUM, A.ACCT_BALANCE, B.TRANS_NUM, TRANS_AMT, TRANS_DT
FROM TABLE1 A, TABLE2 B
WHERE B.Table1_ID = A.ID
AND A.BATCH_NUM = '400012'
SQL#3:
SELECT A.ROLL_NUM, A.ACCT_BALANCE, B.TRANS_NUM, TRANS_AMT, TRANS_DT
FROM TABLE1 A, TABLE2 B
WHERE B.Table1_ID = A.ID
AND A.BATCH_NUM = TO_NUMBER('400012')
如果Table1中的每个BATCH_NUM都是'400012'并且所有ID在表2中匹配,则计数的预期结果应为500,000。
当我在Oracle(v11或v10)中运行这些查询时,SQL#2似乎需要永远,我不得不在10到15分钟后停止运行查询。 SQL#1和#3似乎在不到一秒的时间内立即返回结果,包含完整的500,000条记录。起初,我认为这是一个索引问题,但添加索引并不能解决问题。我在TOAD和SQL Developer中尝试了这个查询,结果相同。
我在这里不知所措,因为Table1中的BATCH_NUM列是VARCHAR2,您认为数据类型的隐式转换会导致查询速度变慢,而不是比未转换的比较速度快。有人可以解释一下吗?
答案 0 :(得分:4)
我同意使用隐式转换会阻止索引访问路径。
为了确定真正正在进行什么,请执行以下操作:
explain plan for
select ......
/
然后,紧接着,执行:
select * from table(dbms_xplan.display);
并发布结果。
不看执行计划,我说的任何事情都会让人猜测。
哦,请发布表中存在的所有索引的定义。
-Mark
答案 1 :(得分:1)
我猜你已经陷入了认为索引访问速度快的陷阱 全表扫描=慢。
过时的统计信息可能导致执行时间不一致。
您可以使用以下方式检查陈旧的统计数据:
DECLARE
l_objlist DBMS_STATS.objecttab;
BEGIN
DBMS_STATS.gather_schema_stats (ownname => USER,
options => 'LIST STALE',
objlist => l_objlist
);
FOR i IN 1 .. l_objlist.COUNT
LOOP
DBMS_OUTPUT.put_line ( l_objlist (i).objtype
|| ' .. '
|| l_objlist (i).objname
);
END LOOP;
END;
答案 2 :(得分:0)
好的,在与同事一起看着解释计划并查看索引后,我们终于找到了问题。我要感谢Mark Bobak和Kevin Burton对此的投入。这是我们发现的:
有一个索引将BATCH_NUM和另外两列作为主索引。当存在强制转换时,Oracle决定对子表中与父表中的BATCH_NUM和ID匹配的所有Table1_ID执行全表扫描。在这种情况下进行全表扫描相对较快。不,将BATCH_NUM作为字符串的搜索条件 - 这是索引失败并导致整个查询“挂起”的位置。因为BATCH_NUM在任何方面都不是唯一的,所以没有任何隐式或显式转换的条件会导致Oracle尝试使用索引,解释计划显示它尝试对索引进行全范围扫描(如果表大约有500,000到1,000,000行记录)。删除索引实际上有助于解决此问题。
答案 3 :(得分:0)
如果必须进行从numeric到string的类型转换,则不能使用列上的“普通”索引。因此,如果您查看第二个查询的解释计划,您可能会看到使用限定条件to_string(batch_num)=='400012'进行全表扫描。
如果您真的必须能够对字符串进行限定,则可以为该列创建基于函数的索引[1]。如果您可以使用选项1或3中描述的语法,则可以在batch_num列上使用“普通”索引。
[1] - http://docs.oracle.com/cd/E11882_01/appdev.112/e25518/adfns_indexes.htm