9

在查询 1 上,即使 id 是索引列,也会执行全表扫描。查询 2 实现了相同的结果,但速度更快。如果查询 1 运行返回一个索引列,则它会快速返回,但如果返回非索引列或整行,则查询需要更长的时间。

在查询 3 中,它运行得很快,但列 'code' 是 VARCHAR2(10) 而不是 NUMBER(12),并且索引方式与 'id' 相同。

为什么查询 1 没有发现它应该使用索引?是否应该更改某些内容以允许索引数字列更快地执行?

[查询1]

select a1.*
from people a1
where a1.id like '119%' 
and rownum < 5

解释计划
SELECT STATEMENT ALL_ROWS
成本:67 字节:2,592 基数:4
2 COUNT STOPKEY
    1 TABLE ACCESS FULL TABLE people
     成本:67 字节:3,240 基数:5

[查询2]

select a1.*
from people a1, people a2
where a1.id = a2.id
and a2.id like '119%' 
and rownum < 5

解释计划
SELECT STATEMENT ALL_ROWS
成本:11 字节:2,620 基数:4
5 COUNT STOPKEY
    4 表访问按索引行表人员
    成本:3 字节:648 基数:1
        3 NESTED LOOPS
        成本:11 字节:2,620 基数:4
            1 INDEX FAST FULL扫描索引 people_IDX3
            成本:2 字节:54,796 基数:7,828
            2 索引范围 扫描索引 people_IDX3
            成本:2 基数:1

[查询3]

select a1.*
from people a1
where a1.code like '119%' 
and rownum < 5

解释计划
SELECT STATEMENT ALL_ROWS
成本:6 字节:1,296 基数:2
   3 COUNT STOPKEY
      2 表访问按索引行表人员
      成本:6 字节:1,296 基数:2
         1 INDEX RANGE SCAN INDEX people_IDX4
         成本:3 基数:2

4

5 回答 5

14

LIKE 模式匹配条件期望将字符类型视为左侧和右侧操作数。当它遇到一个 NUMBER 时,它会隐式地将其转换为 char。您的 Query 1 基本上被默默地改写为:

SELECT a1.*
  FROM people a1
 WHERE TO_CHAR(a1.id) LIKE '119%'
   AND ROWNUM < 5

在您的情况下会发生这种情况,这很糟糕,原因有两个:

  1. 每一行都执行转换,速度很慢;
  2. 由于 WHERE 谓词中有一个函数(尽管是隐式的),Oracle 无法使用A1.ID列上的索引。

要绕过它,您需要执行以下操作之一:

  1. 在列上创建基于函数的索引A1.ID

    CREATE INDEX people_idx5 ON people (TO_CHAR(id));

  2. 如果您需要匹配 ID 列的前 3 个字符的记录,请创建另一个仅包含这 3 个字符的 NUMBER 类型的列,并在其上使用纯=运算符。

  3. 创建一个单独ID_CHAR的类型列VARCHAR2并用TO_CHAR(id). 索引它并使用而不是ID在您的WHERE情况下使用。

    当然,如果您选择基于现有 ID 列创建附加列,则需要保持这 2 个同步。您可以将其作为单个 UPDATE 或在 ON-UPDATE 触发器中批量执行,或将该列添加到适当的代码中的 INSERT 和 UPDATE 语句。

于 2009-11-05T01:22:56.997 回答
4

LIKE 是一个字符串函数,因此不能轻易使用数字索引。在数字索引中,您将拥有 119,120,130,..,1191,1192,1193...,11921,11922... 等。也就是说,所有以“119”开头的行都不会在同一个地方,所以必须读取整个索引(因此是 FAST FULL SCAN)。在基于字符的索引中,它们将在一起(例如'119'、'1191'、'11911'、'120'、...),因此可以使用更好的RANGE SCAN。

如果您要查找特定范围内的 id 值(例如 119000 到 119999),则将其指定为谓词(id 介于 119000 和 119999 之间)。

于 2009-11-04T21:49:12.730 回答
1

优化器决定进行表扫描更快,这很可能是由于实际记录数量较少。

此外,您应该知道非精确匹配总是比精确匹配更糟糕。如果你的 where 是“a1.id='123456'”,它很可能会使用 index.id。但话又说回来,即使索引也需要两次读取(首先在索引中找到一条记录,然后从表中读取块),对于非常小的表,它可以决定进行表扫描。

于 2009-11-04T19:42:40.723 回答
0

尝试在您的一个查询中放置一个提示以强制它使用所需的索引,然后检查您的计划:可能是(由于倾斜或其他原因)优化器确实考虑了索引,但决定不使用它是因为感知成本。

于 2009-11-04T19:39:24.350 回答
-3

LIKE关键字告诉 SQL 您正在进行正则表达式匹配。在您检查可用的字符串函数以查看查询是否可以简单地用它们表示之前,您永远不应该在 SQL 或任何编程库中使用正则表达式。在这种情况下,您可以通过仅比较由代码的前 3 个字符组成的子字符串来将其更改为等于条件。在 Oracle 中,这看起来像:

SELECT *
FROM people
WHERE SUBSTR(code,1,3) = '119'
于 2009-11-04T19:58:33.150 回答