关于“ORDER BY”和“LIKE”子句的性能调整

时间:2010-09-24 07:09:48

标签: performance oracle plsql

我有2个表有很多记录(比如TableA和TableB都有大约3,000,000条记录).vr2_input是用户输入的varchar输入参数,我想得到最多200个“dateField”的TableA记录, stringField类似于'vr2_input'。这两个表连接如下:

select * from(   
   select * from 
        TableA join TableB on TableA.id = TableB.id
        where  TableA.stringField like 'vr2_input' || '%'
        order by  TableA.dateField desc   
) where rownum < 201

查询很慢,我对此进行了调查,发现它是因为“喜欢”和“排序依据”涉及全表扫描。但是,我找不到解决方案来解决问题。我该如何调整这种类型的SQL?我已经在TableA.stringField和TableA.dateField上创建了一个索引但是如何在select语句中使用索引功能呢?数据库是oracle 10g。非常感谢!!

更新:我使用iddqd的建议,只选择我想要的字段并运行解释计划。完成查询大约需要4分钟。 IX_TableA_stringField是TableA.srv_ref字段的索引名称。我再次运行没有提示的解释计划,解释计划仍然得到相同的结果。

EXPLAIN PLAN FOR
    select * from(
         select   
                 /*+ INDEX(TableB IX_TableA_stringField)*/ 
                  TableA.id,
                    TableA.stringField,
                    TableA.dateField,
                    TableA.someField2,
                   TableA.someField3,
            TableB.someField1,
            TableB.someField2,
            TableB.someField3,
                    from TableA 
                    join TableB  on  TableA.id=TableB.id
                    WHERE TableA.stringField  like '21'||'%'  
                 order by TableA.dateField  desc
        ) where rownum < 201

PLAN_TABLE_OUTPUT                                                                                                                                                                                                                                                                                            
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ 
Plan hash value: 871807846                                                                                                                                                                                                                                                                    

--------------------------------------------------------------------------------------------------------                                                                                                                                                                           
| Id  | Operation                       | Name                 | Rows  | Bytes | Cost (%CPU)| Time     |                                                                                                                                                                      
--------------------------------------------------------------------------------------------------------                                                                                                                                                                           
|   0 | SELECT STATEMENT                |                      |   200 | 24000 |  3293   (1)| 00:00:18 |                                                                                                                                                                   
|*  1 |  COUNT STOPKEY                  |                      |       |       |            |          |                                                                                                                                                                                   
|   2 |   VIEW                          |                      |  1397 |   163K|  3293   (1)| 00:00:18 |                                                                                                                                                                             
|*  3 |    SORT ORDER BY STOPKEY        |                      |  1397 | 90805 |  3293   (1)| 00:00:18 |                                                                                                                                                              
|   4 |     NESTED LOOPS                |                      |  1397 | 90805 |  3292   (1)| 00:00:18 |                                                                                                                                                                      
|   5 |      TABLE ACCESS BY INDEX ROWID| TableA       |  1397 | 41910 |   492   (1)| 00:00:03 |                                                                                                                                                 
|*  6 |       INDEX RANGE SCAN          | IX_TableA_stringField |  1397 |       |     6   (0)| 00:00:01 |                                                                                                                                                         
|   7 |      TABLE ACCESS BY INDEX ROWID| TableB      |     1 |    35 |     2   (0)| 00:00:01 |                                                                                                                                                      
|*  8 |       INDEX UNIQUE SCAN         | PK_TableB   |     1 |       |     1   (0)| 00:00:01 |                                                                                                                                                            
--------------------------------------------------------------------------------------------------------                                                                                                                                                                           

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

   1 - filter(ROWNUM<201)                                                                                                                                                                                                                                                                      
   3 - filter(ROWNUM<201)                                                                                                                                                                                                                                                                      
   6 - access("TableA"."stringField" LIKE '21%')                                                                                                                                                                                                                                                 
       filter("TableA"."stringField" LIKE '21%')                                                                                                                                                                                                                                                     
   8 - access(TableA"."id"="TableB"."id")       

8 个答案:

答案 0 :(得分:3)

您说运行查询大约需要4分钟。 EXPLAIN PLAN输出显示估计为18秒。因此,在这种情况下,优化器可能远远没有达到某些估计值。 (它仍然可以选择最好的计划,但也许不是。)

这样的案例的第一步是获取实际的执行计划和统计数据。使用提示/*+ gather_plan_statistics */运行查询,然后立即执行select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST'))

这将显示已运行的实际执行计划,并且对于每个步骤,它将显示估计的行,实际行和实际所用时间。在这里发布输出,也许我们可以对你的问题说些更有意义的事。

如果没有这些信息,我的建议是尝试以下重写查询。我相信它是等价的,因为看起来ID是TableB的主键。

select TableA.id,
       TableA.stringField,
       TableA.dateField,
       TableA.someField2,
       TableA.someField3,
       TableB.someField1,
       TableB.someField2,
       TableB.someField3,
  from (select * from(
         select   
                  TableA.id,
                    TableA.stringField,
                    TableA.dateField,
                    TableA.someField2,
                    TableA.someField3,
                    from TableA 
                    WHERE TableA.stringField  like '21'||'%'  
                 order by TableA.dateField  desc
          )
          where rownum < 201
       ) TableA
       join TableB  on  TableA.id=TableB.id

答案 1 :(得分:1)

您需要选择所有列(*)吗?如果选择所有列,优化器将更有可能进行全扫描。如果您需要输出中的所有列,最好在内联视图中选择id,然后再联接以选择其他列,这可以通过索引查找来完成。尝试为两种情况运行解释计划,以查看优化程序正在执行的操作。

答案 2 :(得分:0)

在stringField和dateField列上创建索引。 SQL引擎会自动使用它们。

答案 3 :(得分:0)

表A具有多少记录,如果它是较小的表,您可以对该表执行选择,然后循环检索表B记录的结果,因为selectA和sort都在TableA上。

一个好的实验是删除连接并测试速度,如果允许的话,你可以把rownum&lt; 201作为主查询的AND子句。目前可能是查询将所有行返回到外部查询然后它被修剪了?

答案 4 :(得分:0)

select id from(   
   select /*+ INDEX(TableB stringField_indx)*/ TableB.id from 
        TableA join TableB on TableA.id = TableB.id
        where  TableA.stringField like 'vr2_input' || '%'
        order by  TableA.dateField desc   
) where rownum < 201

next:

SELECT * FROM TableB WHERE id iN( id from first query)

请发送此表的统计数据和DDL。

答案 5 :(得分:0)

如果你有足够的内存,你可以提示查询使用散列连接。你能否附上解释计划

答案 6 :(得分:0)

您可以在tableA上创建一个函数索引。这将根据条件TableA.stringField返回1或0,如'vr2_input'|| '%'是否满意。该索引将使查询运行得更快。该函数的逻辑将是

if (substr(TableA.stringField, 1, 9) = 'vr2_input'
THEN 
    return 1;
else 
    return 0;

使用实际列名而不是“*”可能会有所帮助。至少应删除常用列名。

答案 7 :(得分:0)

要优化like谓词,可以创建上下文索引并使用contains子句。

看:http://docs.oracle.com/cd/B28359_01/text.111/b28303/ind.htm

由于