优化Oracle查询

时间:2010-03-21 11:55:40

标签: sql database oracle optimization

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

虽然在解释计划中我没有看到使用索引/主键。那是问题吗?

6 个答案:

答案 0 :(得分:3)

试试这个:

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';

如果TRUNCATE()已经有dob已经没有时间,那么请删除mbr,从它的外观(出生日期?)可能不会。过去,你需要一些索引工作。如果您以这种方式查询那么多,我会在2列索引中对head和{{1}}进行索引,如果您说明了这些列的含义,那么它将有助于确定最佳索引。< / p>

答案 1 :(得分: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满足整个查询,这将快得多。

最后,您的统计数据有多新鲜?如果您的统计信息过时,可能会导致优化程序做出决定。例如,更准确的统计信息可能会导致优化器使用复合索引,即使只有两个前导列。

答案 2 :(得分:1)

您应该将文字转换为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' 

答案 3 :(得分: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';

答案 4 :(得分:0)

请在此查询中提供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)) 

答案 5 :(得分:0)

试试这个:

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的可能索引。