ROWNUM 在分页查询中如何工作?

Posted

技术标签:

【中文标题】ROWNUM 在分页查询中如何工作?【英文标题】:How ROWNUM works in pagination query? 【发布时间】:2015-07-31 00:52:51 【问题描述】:

所以我想在 Oracle 数据库中选择一系列行。我需要这样做,因为我在表中有数百万行,并且我想将结果分页给用户(如果您知道在客户端执行此操作的另一种方法,如果重要,我将使用 JavaFX,但我不认为通过网络发送所有数据以在客户端对它们进行分页是个好主意。

所以在阅读了这篇文章后:SQL ROWNUM how to return rows between a specific range,我有以下查询:

Select * From (Select t.*, rownum r from PERSON t) Where r > 100 and r < 110;

100110 只是示例。在应用程序中,我只要求下限并添加 10_000 的大小来获取接下来的 10_000 行。

现在 rownum 列出现在结果中,我不想看到它。由于我对 SQL 不是很有经验,所以这是我的问题:

    为什么(这是我在搜索 SO 之前的第一次尝试)Select * From Person Where rownum &gt; 100 and rownum &lt; 110; 返回 0 行?

    为什么没有像Select ... FROM ... WHERE rownum BETWEEN lowerBound AND upperBound 这样的简单方法?

    如何去掉结果值中的r 列?从那里SQL exclude a column using SELECT * [except columnA] FROM tableA? 我显然需要创建一个视图或一个临时表,但是考虑到我的查询还有其他方法吗?

    它是否确保正确的分页?我阅读了this article 部分“使用 ROWNUM 分页”,它说我应该通过独特的方式对值进行排序以获得一致的分页(所以我想通过rownum 进行排序很好,如果你能确认的话)。这不是违背了使用FIRST_ROWS(N)的目的吗?

我希望不要太多,我可以分成不同的问题,但我认为将它们折叠起来是相关的,因为它们密切相关。

【问题讨论】:

另见Paging with Oracle 【参考方案1】:

问题2的答案:在Oracle 12中可以使用分页

select owner, object_name, object_id
from t
order by owner, object_name
OFFSET 5 ROWS FETCH NEXT 5 ROWS ONLY;

【讨论】:

【参考方案2】:

您有 4 个问题,都围绕 ROWNUM 的用法和功能。我会一一回答每个问题。

为什么(这是我搜索 SO 之前的第一次尝试) Select * From Person Where rownum > 100 and rownum

Thomas Kyte 关于 ROWNUM 和分页 here 的精彩解释。

ROWNUM 值在通过查询的谓词阶段 之后但在查询进行任何排序或聚合之前分配给行。此外,ROWNUM 值仅在分配后才会递增,这就是以下查询永远不会返回行的原因:

select * 
  from t 
 where ROWNUM > 1;

因为第一行的 ROWNUM > 1 不成立,所以 ROWNUM 不会前进到 2。因此,没有 ROWNUM 值会大于 1。

为什么没有简单的方法来执行 Select ... FROM ... WHERE rownum BETWEEN lowerBound AND upperBound 之类的操作?

是的,有。从 Oracle 12c 开始,您可以使用新的 Top-n Row 限制 功能。 See my answer here.

例如,以下查询将按升序返回第四高第七高薪之间的员工:

SQL> SELECT empno, sal
  2  FROM   emp
  3  ORDER BY sal
  4  OFFSET 4 ROWS FETCH NEXT 4 ROWS ONLY;

     EMPNO        SAL
---------- ----------
      7654       1250
      7934       1300
      7844       1500
      7499       1600

SQL>

如何去掉结果值中的 r 列?

在外部查询中列出所需的列名,而不是 select *。对于频繁使用的查询,创建视图是一项简单的一次性活动。

或者,在 SQL*Plus 中,您可以使用 NOPRINT 命令。它不会显示您不想显示的列名。但是,它只适用于 SQL*Plus。

例如,

COLUMN column_name NOPRINT

例如,

SQL> desc dept
 Name                                      Null?    Type
 ----------------------------------------- -------- ------------
 DEPTNO                                             NUMBER(2)
 DNAME                                              VARCHAR2(14)
 LOC                                                VARCHAR2(13)

SQL> COLUMN dname NOPRINT
SQL> COLUMN LOC NOPRINT
SQL> SELECT * FROM dept;

    DEPTNO
----------
        10
        20
        30
        40

SQL>

它是否确保正确的分页?

是的,如果你正确编写了分页查询。

例如,

SELECT val
FROM   (SELECT val, rownum AS rnum
        FROM   (SELECT val
                FROM   t
                ORDER BY val)
        WHERE rownum <= 8)
WHERE  rnum >= 5;

       VAL
----------
         3
         3
         4
         4

4 rows selected.

SQL>

或者,使用 12c 上的新行限制功能,如上所示。

几个很好的例子here。

【讨论】:

这是我从 SO 那里得到的最佳答案之一......我希望我能投票两次 :-) 只是Oracle12的新特性不需要我按唯一属性排序吧? 当您需要排序输出时,总是需要排序。如果没有订购,则无法保证订购。新功能的好处是您不需要子查询来进行第一次排序,您可以在同一个查询中进行排序并选择一个范围。 但是如果没有顺序,它会以与 Select * From tableName 相同的顺序返回行?这是我在 SQL Developer 中运行这两个查询时观察到的行为,但我不确定是否总是如此。 没有订单将毫无意义。这只是随机的。请记住,除非您明确提及 order by,否则没有默认顺序。 select * from table 将以随机方式为您提供行。行从不按顺序存储,因此当您检索它们时,它们是随机获取的。仅当您提及 order by 时才能保证订单。所以,没有 order by 它只是得到随机的行。【参考方案3】:

我通常这样写一个查询:

select * 
from 
(
    select a.*, rownum as rn  
    from table_name a
    where rownum <= 110
)
where rn > 100 

【讨论】:

以上是关于ROWNUM 在分页查询中如何工作?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用猫鼬和EJS实现分页并在分页时保留搜索查询?

pageHelper分页查询oracle数据插入mysql时数据量变少

Oracle 分页查询rownum的用法

orcale rownum 分页查询的数据顺路混乱

oracle分页详解(rownum与orderby)

oracle分页查询 :rownum的使用