我今天遇到了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,则原始查询会立即返回。
答案 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
开头的字符串,然后有从00001
到30000
的30,000个连续数值,但优化器并不那么聪明。它看到一个13个字符的字符串,并且无法弄清楚最后10个字符总是数字所以只有10个可能的值而不是256个(假设8位字符)并且前8个字符总是将成为相同的恒定值。另一方面,如果actionhistory_id
被定义为NUMBER
且值介于1和30000之间,则优化程序可以更加轻松地对各种谓词的选择性进行合理估计。