sql - 根据索引列在 Oracle SQL 中查找 "next 25 rows"

标签 sql oracle performance query-optimization

我有一个大表(约 2 亿行),它在数字列 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 个“前一个”行。但是,这可能需要几分钟时间,并且解释计划始终包含全表扫描。全表扫描对于我的目的来说太昂贵了,但我似乎没有做任何事情来提示查询优化器利用索引(当然,简而言之,将上面的“>=”更改为等号,这表明该索引存在并且可以运行)。我尝试了几种提示都无济于事(索引,index_asc 的几种排列)。

我想做的事是不可能的吗?如果我试图在我有更多控制权的大型数据结构上执行此操作,我会在索引列的值和树上构建一个链表以找到正确的入口点。那么遍历该列表的成本将非常低(是的,我可能必须遍历整个磁盘才能找到我要查找的记录,但我肯定不必扫描整个表)。

如果我正在使用的数据库正在运行 Oracle 数据库 11g 企业版 11.2.0.3.0 - 64 位对我的查询很重要,我将添加。

最佳答案

我构建了一个包含 10K 行的小型测试用例。当我填充表使得 Z 值已经排序时,您给出的确切查询倾向于使用索引。但是当我用随机值填充它并刷新表统计信息时,它开始进行全表扫描,至少对于一些大于 25 的 n 值。所以有一个临界点,优化器决定它的工作量将查找索引条目然后查找表中的相应行比执行全扫描的工作量要多。 (当然,它的估计可能是错误的,但这是它必须继续下去的。)

我注意到您正在使用 SELECT *,这意味着查询返回了两列。这意味着必须访问实际的表行,因为两个索引都不包含这两个列。这可能会促使优化器更喜欢对更大的样本进行全表扫描。如果仅通过索引就可以完成查询,则更有可能使用索引。

一种可能是您根本不需要返回K 的值。如果是这样,我建议您将两次出现的 SELECT * 更改为 SELECT z。在我的测试中,此更改导致一直执行全表扫描的查询改用索引扫描(并且根本不访问表本身)。

如果您确实需要在结果中包含 K,那么您可以尝试在 (Z, K) 上创建索引。该索引可用于在不访问表的情况下满足查询。

关于sql - 根据索引列在 Oracle SQL 中查找 "next 25 rows",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34030059/

相关文章:

Oracle:查询不可见列的注释

MySQL,等于还是不等于?

mysql - 使用 while 循环时出错/将 10 年添加到 MySQL 数据库中的所有日期

java - 在 JDBC 或 Spring JDBC 中使用 REF CURSOR 的替代方案?

html - 在加载 <IMG> 标记中的所有图像之前,不应加载 ngOnInit 或 ngAfterViewInit 中的内容

c++ - I/O 完成端口与 QueueUserApc?

sql - 替代 DISTINCT 函数

mysql - 如何将多个 SELECT 输出输入到 MySQL 的 FROM 子句中?

asp.net - 尝试连接三个表时,SQL 查询不检索数据

java - 按yearWeek或week分组时出现IllegalStateException