【问题标题】:Oracle pagination ROWNUM column>=value challengeOracle 分页 ROWNUM 列>=值挑战
【发布时间】:2014-12-18 21:27:56
【问题描述】:

在使用 oracle 分页时遇到了一些问题。案例:

超过 10 亿行的表:

  • 测量(Id 编号、分类 VARCHAR、值 NUMBER)

索引:

  • 开启测量(值)

我需要一个查询来获取第一个匹配项以及按值排序的以下 2000 个匹配项。我也想使用索引。

第一个想法:

SELECT * FROM Measurement WHERE Value >= 1234567890 
AND ROWNUM <= 2000 ORDER BY Value ASC

结果: 该查询只返回它可以在表中找到的前 2000 个案例,从顶部开始,其中 Value 大于或等于 1234567890,然后对该结果集进行升序排序。

第二个想法:

SELECT * FROM 
(SELECT * FROM Measurement WHERE Value >= 1234567890 ORDER BY Value ASC)
 WHERE ROWNUM <= 2000

结果: Oracle 不理解 ROWNUM 应该限制内部查询的数量,因此 oracle 决定首先获取 Value 大于或等于 1234567890 的所有行,然后在返回前 2000 行之前对该巨大的结果集进行排序。因为 Oracle 猜测表中的大部分数据都会被返回,所以它也忽略了对索引的任何使用。

这些方法都不可接受,因为第一个给出错误的结果,而第二个需要几个小时。

Oracle 完全支持分页吗?

【问题讨论】:

  • 第一个查询肯定不行。第二个查询,删除 hte order by,并将其移至外部选择。尝试在内部查询中添加索引提示并运行解释计划。
  • 你的表分区了吗?第一个给出了不正确的结果,因为逻辑不正确。第二个很慢,因为你做的是正确的事情......查询的解释计划是什么?您无需分页 10 亿行。没有人会读到它们,所以不要……
  • 您不能将 ORDER BY 移动到外部 SELECT @OldProgrammer;你会得到与第一个查询相同的结果。
  • Ben:基本上我想看看上面的测量值,包括 1234567890 测量值。但仅限于 2000 行。看起来不像是一个不寻常的案例。
  • 如果您只想要前 2k 条记录,则无需分页!全扫描根本不是我所期望的。由于 ROWNUM 限制,至少我希望有一个计数停止键,所以你确定吗?是否启用了分区?`

标签: oracle oracle11g pagination rownum


【解决方案1】:

你可以使用下面的

SELECT * FROM 
(SELECT Id, Classification, Value, ROWNUM Rank FROM Measurement WHERE Value >= 1234567890)
 WHERE Rank <= 2000
order by Rank

您不需要在子查询中订购。根本没必要。 以上不是分页,而是我想的第一页。

【讨论】:

  • 这个查询和第二个想法有同样的弱点。内部查询可能会导致数亿行首先放入临时表中,然后外部选择对其进行排序并返回 2000 行。
  • 你有十亿条记录!您将不得不面对性能问题。这是一个解决方案,尽管在您看来可能不是最好的
  • 很多数据库都支持实数限制。与索引数据库相结合,不需要进行超过 2000 次读取即可产生结果。您只需要一个游标,它会遍历索引中的 2000 个值。
【解决方案2】:

不确定您是否找到了解决问题的方法,但要花我的两分钱:

第一个查询不会满足您的要求,因为它将获取 2000 条满足您查询的随机记录,然后进行排序。

进入第二个查询:

Oracle 将首先执行第二个查询,然后只移动到外部查询。所以,rownum 过滤器只有在内部查询执行后才会被应用。

你可以尝试下面的方法,做INDEX FAST FULL SCAN,我已经在一个有 276 万行的表上测试了它,它比其他方法成本更低:

     SELECT * from Measurement
                where value in ( SELECT VALUE FROM 
                                  (SELECT Value FROM Measurement
                                    WHERE Value >= 1234567890 ORDER BY Value ASC)
                                    WHERE ROWNUM <= 2000)

希望对你有帮助

维沙德

【讨论】:

  • 这绝对是一个改进,因为内部选择使用索引并且只返回一个列。但是,它仍然可能读取数亿个索引值,而不是它需要的。 Oracle IN 语句也限制为 1000 个条目。
  • 如果您传递文字而不是使用内部查询传递值,则存在“IN”限制。此外,您可以尝试存在而不是 IN,但一般方法将保持不变。
  • 不过,所需的解决方案还是让 oracle 执行以下操作: 1. 根据索引搜索转到第一个匹配项。 2. 遍历索引中接下来的 2000 个值(已排序)。 3. 对于索引中的 2000 个值中的每一个,返回它所指向的行。
  • 应该是。但不要认为在目前的结构下这是可能的。有一个简单的问题,“VALUE”列是该表中的唯一键吗?
  • 不,它不是唯一的。相同的值可能会出现多次。此外,值的分布是未知的,因此值 1234567890 具有未知的相邻值。
【解决方案3】:

我想我喜欢一个潜在的解决方案。但是,这不是查询。

declare
cursor c is
SELECT * FROM Measurement WHERE Value >= 1234567890 ORDER BY Value ASC;
l_rec c%rowtype;
begin
    open c;
    for i in 1 .. 2000
    loop
        fetch c into l_rec;
        exit when c%notfound;
    end loop;
    close c;
end;
/

【讨论】:

    【解决方案4】:

    请尝试更多选项

    SELECT  *
       FROM( SELECT /*+ FIRST_ROWS(2000) */   
              Id,
              Classification,
              Value,
              ROW_NUMBER() OVER (ORDER BY Value) AS rn
         FROM Measurement
         where Value > 1234567889 
           )
       WHERE rn <=2000;
    

    Update1:-强制在Value上使用索引。这里IDX_ON_VALUE是Value in Measurement上的索引名称

    SELECT * FROM 
                (SELECT /*+ INDEX(a IDX_ON_VALUE) */* FROM Measurement
                 a WHERE value >=1234567890 ) 
                    ORDER BY a.Value ASC)
    WHERE ROWNUM <= 2000
    

    【讨论】:

    • 我们尝试了 /*+ FIRST_ROWS(2000) */ 但它似乎没有效果。在实际限制结果集之前,Oracle 仍会从内部选择中收集太多结果。我们将尝试 ROW_NUMBER() OVER (ORDER BY Value) 并查看它可能产生的影响。我预测这个查询会很慢,因为限制发生在外部查询上。
    • /*+ FIRST_ROWS(2000) */ 在使用 ROW_NUMBER() 进行测试时使用此提示
    • 似乎内部选择仍然在整个表中收集所有大于 1234567890 的值。
    • 该值出现在中间某处,+/- 10%
    • 同样,这里的问题是内部选择返回表的大部分。