我有一个大型表(约200M行),它在数字列Z上编制索引。键列上还有一个索引,K。
K Z
= ==========================================
1 0.6508784068583483336644518457703156855132
2 0.4078768075307567089075462518978907890789
3 0.5365440453204830852096396398565048002638
4 0.7573281573257782352853823856682368153782
我需要做的是找到“围绕”给定记录的25条记录。例如,从K = 3开始的“下一个”记录将是K = 1,然后是K = 4.
我有几个来源(最值得注意的是来自佛罗里达州立大学某些人的this paper),以下SQL应该有效。不难想象沿着索引列以升序或降序扫描是有效的。
select * from (
select *
from T
where Z >= [origin's Z value]
order by Z asc
) where rownum <= 25;
理论上,这应该找到25个“下一个”行,类似的变体会找到25个“前一个”行。但是,这可能需要几分钟,解释计划始终包含全表扫描。对于我的目的,全表扫描实在太贵了,但我没做什么似乎提示查询优化器利用索引(当然,将上面的“&gt; =”更改为等号表示简短,这表明索引存在且可操作)。我尝试了几个提示无效(索引,index_asc在几个排列中)。
我想做的不可能吗?如果我试图在我有更多控制权的大型数据结构上执行此操作,我将在索引列的值和树上构建链接列表以找到正确的入口点。然后遍历列表将非常便宜(是的,我可能必须在整个磁盘上运行才能找到我正在寻找的记录,但我肯定不必扫描整个表格。)
我将添加以防我的查询对我正在使用的数据库运行Oracle Database 11g企业版11.2.0.3.0版 - 64位非常重要。
答案 0 :(得分:8)
我构建了一个10K行的小测试用例。当我填充表格以使Z值已经被排序时,您给出的确切查询倾向于使用索引。但是当我用随机值填充它并刷新表统计信息时,它开始进行全表扫描,至少对于大于25的n值。因此优化器确定了工作量的临界点将查找索引条目然后查找表中的相应行是否超过完成扫描的工作量。 (当然,它的估计可能是错误的,但这就是它必须继续下去的。)
我注意到您正在使用SELECT *
,这意味着查询将返回两列。这意味着必须访问实际的表行,因为这两个索引都不包含这两列。这可能会促使优化器更倾向于对更大的样本进行全表扫描。如果只能从索引中完成查询,则更有可能使用索引。
一种可能性是您根本不需要返回K
的值。如果是这样,我建议您将SELECT *
的出现次数更改为SELECT z
。在我的测试中,此更改导致执行全表扫描的查询改为使用索引扫描(而根本不访问表本身)。
如果您确实需要在结果中加入K
,那么您可以尝试在(Z, K)
上创建索引。该索引可用于满足查询而无需访问表。