【问题标题】:Optimizing Oracle query优化 Oracle 查询
【发布时间】:2011-01-30 00:37:43
【问题描述】:
SELECT MAX(verification_id)
  FROM VERIFICATION_TABLE
 WHERE head = 687422
   AND mbr = 23102
   AND RTRIM(LTRIM(lname)) = '.iq bzw'
   AND  TO_CHAR(dob,'MM/DD/YYYY')= '08/10/2004'
   AND system_code = 'M';

此查询需要 153 秒才能运行。 VERIFICATION_TABLE 中有数百万行。

我认为查询需要很长时间,因为 where 子句中的函数。但是,我需要在列上执行 ltrim rtrim 并且日期必须以MM/DD/YYYY 格式匹配。如何优化此查询?

解释计划:

SELECT STATEMENT, GOAL = ALL_ROWS           80604   1   59
 SORT AGGREGATE                                   1   59
  TABLE ACCESS FULL P181    VERIFICATION_TABLE  80604   1   59

主键:

VRFTN_PK    Primary VERIFICATION_ID 

索引:

N_VRFTN_IDX2    head, mbr, dob, lname, verification_id
N_VRFTN_IDX3    last_update_date
N_VRFTN_IDX4    mbr, lname, dob, verification_id
N_VRFTN_IDX4    verification_id

不过,在解释计划中,我没有看到正在使用索引/主键。是这个问题吗?

【问题讨论】:

  • 你的表有主键或索引吗?应该不会花那么多时间....

标签: sql database oracle optimization


【解决方案1】:

检查 HEAD 和 MBR 的数据类型。 值“687422 和 23102”具有非常有选择性的“感觉”。也就是说,如果您有数十万个 head 值和表中的数百万条记录,那么 HEAD 似乎很有选择性。 [虽然这可能完全是误导。]

无论如何,您可能会发现 HEAD 和/或 MBR 实际上存储为 VARCHAR2 或 CHAR 字段而不是 NUMBER。如果是这样,将字符与数字进行比较将阻止使用索引。尝试以下方法(我已经包含了 dob 谓词与日期的转换,但添加了显式格式掩码)。

SELECT MAX(verification_id)
  FROM VERIFICATION_TABLE
 WHERE head = '687422'
   AND mbr = '23102'
   AND RTRIM(LTRIM(lname)) = '.iq bzw'
   AND TRUNCATE(dob) = TO_DATE('08/10/2004','MM/DD/YYYY')
   AND system_code = 'M';

【讨论】:

    【解决方案2】:

    唯一可能在您的查询中使用的索引是 N_VRFTN_IDX2,因为它索引了您在 WHERE 子句中使用的四个列:HEAD、MBR、DOB 和 LNAME。

    但是,由于您对 DOB 和 LNAME 都应用了函数,因此它们没有资格被考虑。然后优化器可能决定不使用该索引,因为它认为 HEAD+MBR 本身是一个选择性不足的组合。如果您从 DOB 中删除了 TO_CHAR() 调用,那么您在 N_VRFTN_IDX2 上有三个前导列,这可能使其对优化器更具吸引力。同样,是否有必要 TRIM() LNAME?

    另一件事是,需要查找 SYSTEM_CODE 意味着查询必须从表中读取(因为该列没有被索引)。如果 N_VRFTN_IDX2 的聚类因子很差,优化器可能会决定进行 FULL TABLE SCAN,因为索引读取是开销。而如果您将 SYSTEM_CODE 添加到索引中,整个查询可以通过 INDEX RANGE SCAN 来满足,这会快很多。

    最后,您的统计数据有多新鲜?如果你的统计数据是陈旧的,那可能会导致优化器做出一个愚蠢的决定。例如,更准确的统计数据可能会导致优化器使用复合索引,即使只有两个前导列。

    【讨论】:

      【解决方案3】:

      试试这个:

      SELECT MAX(verification_id)
        FROM VERIFICATION_TABLE
       WHERE head = 687422
         AND mbr = 23102
         AND TRIM(lname) = '.iq bzw'
         AND dob between TO_DATE('08/10/2004') and TO_DATE('08/11/2004')
         AND system_code = 'M';
      

      这样会使用一个可能的 dob 索引。

      【讨论】:

        【解决方案4】:

        您应该将文字转换为 DATE 而不是将列转换为 VARCHAR2,如下所示:

        AND  dob = TO_DATE('08/10/2004','MM/DD/YYYY')
        

        或者使用更好的 ANSI 日期文字语法:

        AND  dob = DATE '2004-08-10'
        

        如果 dob 列包含时间(出生日期通常不包含,可能在医院除外!)那么您可以:

        AND  dob >= DATE '2004-08-10' 
        AND  dob <  DATE '2004-08-11' 
        

        【讨论】:

          【解决方案5】:

          试试这个:

          SELECT MAX(verification_id)
            FROM VERIFICATION_TABLE
           WHERE head = 687422
             AND mbr = 23102
             AND TRIM(lname) = '.iq bzw'
             AND TRUNCATE(dob) = TO_DATE('08/10/2004')
             AND system_code = 'M';
          

          如果dob 没有时间处理它,请删除TRUNCATE(),从它的外观(出生日期?)来看,它可能没有。除此之外,您需要一些索引工作。如果您以这种方式查询那么多,我会在 2 列索引中索引 mbrhead,如果您说列的含义,这将有助于确定最佳索引。

          【讨论】:

          • 是的,我会研究索引。执行您的建议将查询时间从 153 秒缩短到 93 秒。不过还是很贵
          • @Omnipresent - 表有主键吗?它应该使这几乎是即时的
          • 它有主键和索引。我已经和他们一起编辑了问题。不过,我没有看到它们在解释计划中被使用?
          • @Omnipresent - 尝试运行:exec DBMS_STATS.GATHER_TABLE_STATS(null, 'VERIFICATION_TABLE', null, DBMS_STATS.AUTO_SAMPLE_SIZE, false, 'FOR ALL COLUMNS SIZE AUTO'); exec DBMS_STATS.GATHER_INDEX_STATS(null, 'N_VRFTN_IDX2', null, DBMS_STATS.AUTO_SAMPLE_SIZE); 以更新该表和索引的统计信息。如果它不使用该索引,则统计数据似乎已经过时/过时了。需要一点时间,然后再次尝试查询。
          • @Omnipresent - 一次运行一个...我不熟悉 PL/SQL 开发人员的行为方式(这是为 SQL Plus 设计的),但一次运行一个(按顺序!)在命令窗口中应该可以工作。
          【解决方案6】:

          请为此查询提供 EXPLAIN 输出,以便我们知道减速发生的位置。两个想法:

          改变

          AND  TO_CHAR(dob,'MM/DD/YYYY')= '08/10/2004'
          

          AND  dob = <date here, not sure which oracle str2date function you need>
          

          并使用基于函数的索引

          RTRIM(LTRIM(lname)) 
          

          【讨论】:

            猜你喜欢
            • 2015-02-26
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多