如何对表进行索引以优化此Oracle SELECT查询?

时间:2011-09-09 10:35:47

标签: sql performance oracle

我在Oracle10g中得到以下查询:

select * 
  from DATA_TABLE DT, 
       LOOKUP_TABLE_A LTA, 
       LOOKUP_TABLE_B LTB
 where DT.COL_A = LTA.COL_A (+) 
   and DT.COL_B = LTA.COL_B (+) 
   and LTA.COL_C = LTB.COL_C
   and LTA.COL_B = LTB.COL_B
   and ( DT.REF_TXT = :refTxt or DT.ALT_REF_TXT = :refTxt )
   and DT.CREATED_DATE between :startDate and :endDate

并且想知道您是否有任何优化查询的提示。

目前我有以下指数:

IDX1 on DATA_TABLE (REF_TXT, CREATED_DATE)
IDX2 on DATA_TABLE (ALT_REF_TXT, CREATED_DATE)
LOOKUP_A_PK on LOOKUP_TABLE_A (COL_A, COL_B)
LOOKUP_A_IDX1 on LOOKUP_TABLE_A (COL_C, COL_B)
LOOKUP_B_PK on LOOKUP_TABLE_B (COL_C, COL_B)

注意,LOOKUP表非常小(<200行)。

编辑:

解释计划:

Query Plan
SELECT STATEMENT   Cost = 8
  FILTER
    NESTED LOOPS
      NESTED LOOPS
        TABLE ACCESS BY INDEX ROWID DATA_TABLE
          BITMAP CONVERSION TO ROWIDS
            BITMAP OR
              BITMAP CONVERSION FROM ROWIDS
                SORT ORDER BY
                  INDEX RANGE SCAN IDX1
              BITMAP CONVERSION FROM ROWIDS
                SORT ORDER BY
                  INDEX RANGE SCAN IDX2
        TABLE ACCESS BY INDEX ROWID LOOKUP_TABLE_A
          INDEX UNIQUE SCAN LOOKUP_A_PK
      TABLE ACCESS BY INDEX ROWID LOOKUP_TABLE_B
        INDEX UNIQUE SCAN LOOKUP_B_PK

EDIT2:

数据如下所示:

将有10000个不同的REF_TXT,每个都有10-100个CREATED_DT。 ALT_REF_TXT主要是NULL,但是会有100s-1000s,它与REF_TXT不同。

EDIT3:修正了ALT_REF_TXT实际包含的内容。

5 个答案:

答案 0 :(得分:3)

您目前获得的执行计划看起来非常不错。没有明显的改进。

正如其他人所指出的那样,你有一些外连接指示符,但是你基本上通过要求两个外部表中其他列的相等性来阻止外连接。从执行计划中可以看出,没有发生外部联接。如果您不想要外部联接,请删除(+)运算符,它们只会让问题混乱。如果您确实需要外连接,请重写查询,如@Dems。

所示

如果您对当前性能不满意,我建议使用gather_plan_statistics提示运行查询,然后使用DBMS_XPLAN.DISPLAY_CURSOR(?,?,'ALLSTATS LAST')查看实际执行统计信息。这将显示归因于执行计划中每个步骤的已用时间。

将一个或两个查找表转换为索引组织表可能会带来一些好处。

答案 1 :(得分:3)

IDX1和IDX2上的2个索引范围扫描最多会产生100行,因此您的BITMAP CONVERSION TO ROWIDS最多可生成200行。从那以后,它只被rowid索引访问,导致可能的亚秒执行。那么你真的遇到了性能问题吗?如果是这样,它需要多长时间?

如果您遇到性能问题,请遵循Dave Costa的建议并获得真实的计划,因为在这种情况下,您可能正在使用其他计划运行时,可能是由于某些绑定变量值或不同的优化器环境设置。 / p>

的问候,
罗布。

答案 2 :(得分:2)

这是其中一种情况,即在不知道数据意味着什么的情况下尝试优化DBMS性能毫无意义。

对于每个日期,您的DT中是否有许多不同的CREATED_DATE值和几行?如果是这样,你想在CREATED_DATE上有一个索引,因为它将是DBMS拒绝它不想处理的列的主要方式。

另一方面,您是否只有少数日期,还有许多不同的REF_TXT或ALT_REF_TXT值?在这种情况下,您可能有正确的复合索引选择。

查询中存在OR会使事情变得非常复杂,并且会在窗口中抛出大部分猜测。你必须查看EXPLAIN PLAN,看看发生了什么。

如果您有数千万个不同的REF_TXT和ALT_REF_TXT值,您可能需要考虑对此架构进行非规范化。


编辑。 感谢您提供更多信息。你的解释计划中没有我能看到的吸烟枪。如果你对表现不满意,可以尝试下一步。

翻转数据表中复合索引中列的顺序。也许这将使您获得更简单的索引范围扫描,而不是所有位图猴子业务。

将SELECT *替换为查询结果集中实际需要的列的名称。在任何情况下,这都是很好的编程实践,它可以让优化器避免一些工作。

如果事情仍然太慢,请尝试将其重新设置为两个查询的UNION,而不是使用OR。这可能允许查询的alt_ref_txt部分(由该列中的所有NULL值稍微复杂一些)进行单独优化。

答案 3 :(得分:2)

这可能是您想要使用更新语法的查询。

(没有内连接破坏外连接)

select
  * 
from
  DATA_TABLE DT
left outer join
  (
    LOOKUP_TABLE_A LTA
  inner join
    LOOKUP_TABLE_B LTB
      on  LTA.COL_C = LTB.COL_C
      and LTA.COL_B = LTB.COL_B
  )
    on  DT.COL_A = LTA.COL_A
    and DT.COL_B = LTA.COL_B
where
   ( DT.REF_TXT = :refTxt or DT.ALT_REF_TXT = :refTxt )
   and DT.CREATED_DATE between :startDate and :endDate

我拥有的索引是......

LOOKUP_TABLE_A (COL_A, COL_B)
LOOKUP_TABLE_B (COL_B, COL_C)
DATA_TABLE (REF_TXT, CREATED_DATE)
DATA_TABLE (ALT_REF_TXT, CREATED_DATE)


注意:WHERE子句中的第一个条件包含一个可能会破坏INDEXes使用的OR。在这种情况下,我 在UNIONing两个查询中看到了性能优势......

  <your query>
where
   DT.REF_TXT = :refTxt
   and DT.CREATED_DATE between :startDate and :endDate

UNION

  <your query>
where
   DT.ALT_REF_TXT = :refTxt
   and DT.CREATED_DATE between :startDate and :endDate

答案 4 :(得分:0)

使用“set autot trace”提供此查询的输出。让我们看看它拉了多少块。解释计划看起来不错,应该很快。如果需要更多,请将查找表信息非规范化为DT。违反第三范式,但它会通过消除连接使您的查询更快。在毫秒计数的情况下,一切都在缓冲区中,并且您需要该查询运行1000次/秒,它可以通过降低每行查看的块数来帮助。它是提高阅读性能的最终方式,但会使您的应用程序复杂化(并破坏您可爱的ER图)。

相关问题