这对我来说是一个永无止境的话题,我想知道我是否可能忽略了一些东西。本质上,我在应用程序中使用两种类型的 SQL 语句:
- 具有“回退”限制的常规查询
- 排序和分页查询
现在,我们正在讨论对具有数百万条记录的表的一些查询,并与另外 5 个具有数百万条记录的表相连接。显然,我们几乎不想获取所有这些,这就是为什么我们有上述两种方法来限制用户查询。
案例1非常简单。我们只是添加了一个额外的ROWNUM
过滤器:
WHERE ...
AND ROWNUM < ?
这是相当快的,因为 Oracle 的 CBO 会在其执行计划中考虑这个过滤器,并且可能会应用一个FIRST_ROWS
操作(类似于/*+FIRST_ROWS*/
提示强制执行的操作。
但是,情况 2对 Oracle 来说有点棘手,因为没有LIMIT ... OFFSET
其他 RDBMS 中的子句。因此,我们将“业务”查询嵌套在技术包装器中,如下所示:
SELECT outer.* FROM (
SELECT * FROM (
SELECT inner.*, ROWNUM as RNUM, MAX(ROWNUM) OVER(PARTITION BY 1) as TOTAL_ROWS
FROM (
[... USER SORTED business query ...]
) inner
)
WHERE ROWNUM < ?
) outer
WHERE outer.RNUM > ?
请注意,TOTAL_ROWS
即使没有获取所有数据,该字段的计算也是为了知道我们将拥有多少页。现在这个分页查询通常是相当令人满意的。但是时不时地(正如我所说,在查询 5M+ 记录时,可能包括非索引搜索),这会运行 2-3 分钟。
编辑:请注意,潜在的瓶颈不是那么容易规避的,因为必须在分页之前应用排序!
我想知道,是最先进的 模拟LIMIT ... OFFSET
,包括TOTAL_ROWS
在 Oracle 中,还是有更好的解决方案,在设计上会更快,例如通过使用ROW_NUMBER()
窗口函数而不是ROWNUM
伪列?