为什么更改此条件的where子句会大大减少执行时间?

时间:2012-03-22 20:02:45

标签: sql oracle tsql oracle11g

我今天遇到了SQL语句的问题,我可以通过添加其他条件来修复,但是我真的想知道为什么我的更改解决了问题。

问题查询:

SELECT *
FROM
  (SELECT ah.*,
    com.location,
    ha.customer_number,
    d.name applicance_NAME,
    house.name house_NAME,
    dr.name RULE_NAME
FROM actionhistory ah
INNER JOIN community com
ON (t.city_id = com.city_id)
INNER JOIN house_address ha
ON (t.applicance_id   = ha.applicance_id
AND ha.status_cd = 'ACTIVE')
INNER JOIN applicance d
ON (t.applicance_id = d.applicance_id)
INNER JOIN house house
ON (house.house_id = t.house_id)
LEFT JOIN the_rule tr
ON (tr.the_rule_id = t.the_rule_id)
WHERE actionhistory_id    >= 'ACT100010000' 
ORDER BY actionhistory_id
)
WHERE rownum <= 30000;

“修复”

SELECT *
FROM
  (SELECT ah.*,
    com.location,
    ha.customer_number,
    d.name applicance_NAME,
    house.name house_NAME,
    dr.name RULE_NAME
FROM actionhistory ah
INNER JOIN community com
ON (t.city_id = com.city_id)
INNER JOIN house_address ha
ON (t.applicance_id   = ha.applicance_id
AND ha.status_cd = 'ACTIVE')
INNER JOIN applicance d
ON (t.applicance_id = d.applicance_id)
INNER JOIN house house
ON (house.house_id = t.house_id)
LEFT JOIN the_rule tr
ON (tr.the_rule_id = t.the_rule_id)
WHERE actionhistory_id    >= 'ACT100010000' and  actionhistory_id  <= 'ACT100030000'
ORDER BY actionhistory_id
)

所有_id列都是索引序列。 第一个查询的解释计划的成本为372,第二个是14.这是在Oracle 11g数据库上运行。

此外,如果where子句中的actionhistory_id小于ACT100000000,则原始查询会立即返回。

2 个答案:

答案 0 :(得分:3)

这是因为actionhistory_id列上的索引。

在第一次查询期间,Oracle必须返回包含“ACT100010000”之后的记录索引的所有索引块,然后必须将索引与表匹配才能获取所有记录,然后从中获取29999条记录。结果集。

在第二次查询期间,Oracle只需返回包含“ACT100010000”和“ACT100030000”之间记录的索引块。然后它从表中获取索引块中表示的那些记录。在找到索引之后抓取记录的步骤比使用第一个查询时少得多。

注意到关于id是否小于ACT100000000的最后一行 - 听起来这些记录可能都在同一个内存块中(或在一组连续的块中)。

编辑:请同时考虑贾斯汀所说的内容 - 我在谈论实际表现,但他指出id为varchar会大大增加潜在价值(而不是数字)并且估计的计划可能会反映比现实更长的时间,因为优化器在执行之前不知道全部范围。为了进一步优化,考虑到他的观点,你可以在id列上放置一个基于函数的索引,或者你可以将它作为一个组合键,varchar部分在一列中,数字部分在另一列中。

答案 1 :(得分:1)

  • 这两个查询的计划是什么?
  • 您的表格中的统计信息是最新的吗?
  • 两个查询是否返回相同的行集?它们并不明显,但ACT100030000可能是系统中最大的actionhistory_id。这也有点令人困惑,因为第一个查询的actionhistory_id谓词的值为TRA100010000,与第二个查询中的ACT值非常不同。我猜这是一个错字?
  • 您是否在测量获取第一行所需的时间?或者获取最后一行所需的时间?那些经过的时间是什么时候?

我没有这些信息的猜测是,您似乎使用了actionhistory_id列的错误数据类型这一事实正在影响Oracle优化器生成适当基数估算的能力,这可能导致优化器低估谓词的选择性以及生成效果不佳的计划。人类可能会猜测actionhistory_id是一个以ACT10000开头的字符串,然后有从0000130000的30,000个连续数值,但优化器并不那么聪明。它看到一个13个字符的字符串,并且无法弄清楚最后10个字符总是数字所以只有10个可能的值而不是256个(假设8位字符)并且前8个字符总是将成为相同的恒定值。另一方面,如果actionhistory_id被定义为NUMBER且值介于1和30000之间,则优化程序可以更加轻松地对各种谓词的选择性进行合理估计。

相关问题