Oracle SQL Where Condition将varchar2字段与数字或字符串进行比较

时间:2011-10-12 05:53:13

标签: sql oracle

我有两个包含以下列的表:

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,您认为数据类型的隐式转换会导致查询速度变慢,而不是比未转换的比较速度快。有人可以解释一下吗?

4 个答案:

答案 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