2010-03-21 90 views
3
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中有数百万行。优化Oracle查询

我认为查询花了很长时间,因为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 

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

+0

你有表的主键或索引吗?它不应该花很多时间... – 2010-03-21 12:00:49

回答

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已经没有时间了,从它的外观(出生日期?)它可能不是。过去,你需要一些索引工作。如果您在这种风格下查询了很多,我会在列索引中索引mbrhead,如果您说了列的含义,它可以帮助确定最佳索引。

+0

是的,我会研究索引。做你所建议的查询从153秒减少到93秒。仍然昂贵,但 – Omnipresent 2010-03-21 12:11:46

+0

@Omnipresent - 表是否有一个主键?它应该使它几乎是即时的 – 2010-03-21 12:16:37

+0

它有主键和索引。我和他们一起编辑了问题。虽然,我没有看到他们在解释计划中使用? – Omnipresent 2010-03-21 12:23:56

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)) 
+0

提供解释计划 – Omnipresent 2010-03-21 12:12:45

1

你应该把文字转换为日期,而不是列到VARCHAR2这样的:

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

或者使用最好ANSI日期文字语法:如果DOB列包含

AND dob = DATE '2004-08-10' 

时间(通常不会出生的日期,除了大概在医院!),那么你可以做:

AND dob >= DATE '2004-08-10' 
AND dob < DATE '2004-08-11' 
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上的一个可能的索引。

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满足整个查询,这将会快得多。

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

1

检查HEAD和MBR的数据类型。 值“687422和23102”具有相当选择性的“感觉”。也就是说,如果表中有数十万个头和数百万条记录的值,那么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';