【问题标题】:Oracle SQL Where Condition comparing varchar2 field with number or stringOracle SQL Where 条件将 varchar2 字段与数字或字符串进行比较
【发布时间】:2011-10-12 05:53:12
【问题描述】:

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

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 在 Table2 中匹配,则计数的预期结果应该是 500,000。

当我在 Oracle(v11 或 v10)中运行这些查询时,SQL #2 似乎需要很长时间,我不得不在 10 到 15 分钟后停止运行查询。 SQL #1 和 #3 似乎在不到一秒的时间内立即返回了完整的 500,000 条记录的结果。起初,我认为这是一个索引问题,但添加索引并不能解决问题。我在 TOAD 和 SQL Developer 中尝试了这个查询,结果相同。

我在这里不知所措,因为 Table1 中的 BATCH_NUM 列是 VARCHAR2 并且您认为数据类型的隐式转换会导致查询速度变慢,而不是比未转换的比较快。有人可以解释一下吗?

【问题讨论】:

  • 能否请您也发布三个查询的解释计划结果?

标签: sql oracle


【解决方案1】:

我同意使用隐式转换会阻止索引访问路径。

要确定真正发生了什么,请执行以下操作:

explain plan for
select ......
/

然后,紧接着,做:

select * from table(dbms_xplan.display);

并发布结果。

不看执行计划,我说的都是猜测。

哦,请发布表中存在的所有索引的定义。

-马克

【讨论】:

    【解决方案2】:

    我猜你已经陷入了认为索引访问 = 快速的陷阱, 全表扫描 = 慢。

    过时的统计数据可能会导致执行时间不一致。

    您可以使用以下方法检查过时的统计信息:

    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;
    

    【讨论】:

      【解决方案3】:

      好的,在和同事坐下来看了解释计划和索引后,我们终于找到了问题所在。我要感谢 Mark Bobak 和 Kevin Burton 对此的投入。以下是我们的发现:

      有一个以 BATCH_NUM 和另外两列作为主索引的索引。当发生强制转换时,Oracle 决定对与父表中的 BATCH_NUM 和 ID 匹配的子表中的所有 Table1_ID 进行全表扫描。在这种情况下进行全表扫描相对较快。不,使用 BATCH_NUM 作为字符串的搜索条件 - 这是索引失败并导致整个查询“挂起”的地方。因为 BATCH_NUM 在任何方面都不是唯一的,所以没有任何隐式或显式转换的条件会导致 Oracle 尝试使用索引,并且解释计划显示它尝试对索引进行全范围扫描(如果表大约有 500,000 到 1,000,000 行记录)。删除索引实际上有助于解决此问题。

      【讨论】:

        【解决方案4】:

        如果必须进行从数字到字符串的类型转换,则不能使用列上的“普通”索引。因此,如果您查看第二个查询的解释计划,您可能会看到全表扫描的条件为 to_string(batch_num) == '400012'。

        如果您真的必须能够限定字符串,您可以为该列创建一个基于函数的索引[1]。如果您可以使用选项 1 或 3 中描述的语法,则可以利用 batch_num 列上的“正常”索引。

        [1] - http://docs.oracle.com/cd/E11882_01/appdev.112/e25518/adfns_indexes.htm

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2010-12-04
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2021-12-24
          • 1970-01-01
          相关资源
          最近更新 更多