【问题标题】:SQL Query going for Full Table scan instead of Index Based ScanSQL 查询用于全表扫描而不是基于索引的扫描
【发布时间】:2011-03-10 04:32:33
【问题描述】:

我有两张桌子:

create table big( id number, name varchar2(100));
insert into big(id, name) select rownum, object_name from all_objects;

create table small as select id from big where rownum < 10;
create index big_index on big(id);

如果我执行以下查询,则在这些表上:

select * 
  from big_table 
 where id like '45%' 
    or id in ( select id from small_table);

它总是用于全表扫描。

Execution Plan
----------------------------------------------------------
Plan hash value: 2290496975
----------------------------------------------------------------------------
| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |  3737 | 97162 |    85   (3)| 00:00:02 |
|*  1 |  FILTER            |       |       |       |            |          |
|   2 |   TABLE ACCESS FULL| BIG   | 74718 |  1897K|    85   (3)| 00:00:02 |
|*  3 |   TABLE ACCESS FULL| SMALL |     1 |     4 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

1 - filter("ID"=45 OR  EXISTS (SELECT /*+ */ 0 FROM "SMALL" "SMALL"

          WHERE "ID"=:B1))

3 - filter("ID"=:B1)

有什么方法可以重写查询,使其始终用于索引扫描。

【问题讨论】:

  • 这是一个错字 - 认为在数据类型与字符串无关(VARCHAR2 等)的列上使用 LIKE 会出错
  • 所以你想要以 45 开头的 id?比如 45、45029 和 451?

标签: sql oracle indexing


【解决方案1】:

不,不,不。

您不希望它使用索引。幸运的是,Oracle 比这更聪明。

ID 是数字。虽然它的 ID 值可能为 45,450,451,452,4501,45004,4500003 等,但在索引中这些值将分散在任何地方和任何地方。如果您使用 ID BETWEEN 450 AND 459 之类的条件,那么可能值得使用索引。

要使用索引,它必须从上到下一直扫描它(将每个 ID 转换为一个字符以进行 LIKE 比较)。然后,对于任何匹配,它都必须得到 NAME 列。

它决定扫描表更容易、更快捷(因为它有 75,000 行并没有那么大),而不是在索引和表之间来回走动。

【讨论】:

  • 正确。这样做的方法是将 ID 转换为字符串的基于函数的索引。然后 LIKE 会因为缺少更好的术语而表现得“正确”。
【解决方案2】:

其他的都是对的,你不应该使用这样的数字列。

但是,在这种情况下,实际上是 OR &lt;subquery&gt; 构造导致(性能)问题。我不知道它在版本 11 中是否有所不同,但直到版本 10gr2,它会导致一个过滤操作,它基本上是一个带有相关子查询的嵌套循环。在您的情况下,将数字列用作 varchar 也会导致全表扫描。

您可以像这样重写您的查询:

select *
  from big
 where id like '45%'
union all
select *
  from big
  join small using(id)
 where id not like '45%';

使用您的测试用例,我最终得到了 174000 行大行和 9 行小行。 运行您的查询需要 7 秒,获得 1211399 次一致的获取。 运行我的查询 0.7 秒并使用 542 个一致的获取。

我的查询的解释计划是:

 --------------------------------------------------------------------
| Id  | Operation                     | Name   | Rows  | Cost (%CPU)|
---------------------------------------------------------------------
|   0 | SELECT STATEMENT              |        |  8604 |   154   (6)|
|   1 |  UNION-ALL                    |        |       |            |
|*  2 |   TABLE ACCESS FULL           | BIG    |  8603 |   151   (4)|
|   3 |   NESTED LOOPS                |        |     1 |     3   (0)|
|*  4 |    TABLE ACCESS FULL          | SMALL  |     1 |     3   (0)|
|   5 |    TABLE ACCESS BY INDEX ROWID| BIG    |     1 |     0   (0)|
|*  6 |     INDEX UNIQUE SCAN         | BIG_PK |     1 |     0   (0)|
---------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter(TO_CHAR("ID") LIKE '45%')
   4 - filter(TO_CHAR("SMALL"."ID") NOT LIKE '45%')
   6 - access("BIG"."ID"="SMALL"."ID")


Statistics
----------------------------------------------------------
          1  recursive calls
          0  db block gets
        542  consistent gets
          0  physical reads
          0  redo size
      33476  bytes sent via SQL*Net to client
        753  bytes received via SQL*Net from client
         76  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       1120  rows processed

【讨论】:

    【解决方案3】:

    这样的事情可能会奏效:

    select * 
      from big_table big
     where id like '45%' 
        or exists ( select id from small_table where id = big.id);
    

    【讨论】:

    • 嗨 Sunil 我试过了,但得到了相同的结果 执行计划 |身份证 |操作 |姓名 |行 |字节 |成本 (%CPU)|时间 | -------------------------------------------------- -------------------------- | 0 |选择声明 | | 3737 | 97162 | 85 (3)| 00:00:02 | |* 1 |过滤器 | | | | | | | 2 |表访问已满|大 | 74718 | 1897K| 85 (3)| 00:00:02 | |* 3 |表访问已满|小 | 1 | 4 | 3 (0)| 00:00:01 |
    猜你喜欢
    • 1970-01-01
    • 2021-08-12
    • 1970-01-01
    • 2021-03-19
    • 2018-05-05
    • 2012-01-31
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多