如何加速Oracle中的row_number?

Jas*_*ker 11 sql oracle row-number oracle10g

我有一个SQL查询,看起来像这样:

SELECT * FROM(
    SELECT
        ...,
        row_number() OVER(ORDER BY ID) rn
    FROM
        ...
) WHERE rn between :start and :end
Run Code Online (Sandbox Code Playgroud)

从本质上讲,正是ORDER BY部分减慢了速度.如果我要删除它,EXPLAIN成本会下降一个数量级(超过1000倍).我试过这个:

SELECT 
    ...
FROM
    ...
WHERE
    rownum between :start and :end
Run Code Online (Sandbox Code Playgroud)

但这并没有给出正确的结果.有没有简单的方法来加快速度?或者我是否需要花更多时间使用EXPLAIN工具?

Qua*_*noi 12

ROW_NUMBER是非常低效的Oracle.

有关性能详细信息,请参阅我博客中的文章:

对于您的特定查询,我建议您替换它ROWNUM并确保使用索引:

SELECT  *
FROM    (
        SELECT  /*+ INDEX_ASC(t index_on_column) NOPARALLEL_INDEX(t index_on_column) */
                t.*, ROWNUM AS rn
        FROM    table t
        ORDER BY
                column
        )
WHERE rn >= :start
      AND rownum <= :end - :start + 1
Run Code Online (Sandbox Code Playgroud)

此查询将使用 COUNT STOPKEY

还要么确保您column不可为空,要么添加WHERE column IS NOT NULL条件.

否则,索引不能用于检索所有值.

请注意,如果ROWNUM BETWEEN :start and :end没有子查询,则无法使用.

ROWNUM总是最后分配并最后检查,这种方式ROWNUM总是按顺序排列.

如果您使用ROWNUM BETWEEN 10 and 20,满足所有其他条件的第一行将成为返回,临时分配ROWNUM = 1和未通过测试的候选者ROWNUM BETWEEN 10 AND 20.

然后下一行将是候选,分配ROWNUM = 1和失败等,因此,最后,根本不会返回任何行.

这应该通过将ROWNUM's'放入子查询来解决.

  • 这意味着`CBO`足够聪明,可以拿起索引.它实际上是ROWNUM而不是ROW_NUMBER,这在这里很重要. (3认同)

Dav*_*vid 5

看起来像是一个分页查询给我.

从这篇ASKTOM文章(约90%的页面):

您需要按照这些分页查询的唯一内容进行排序,以便每次都确定性地为行分配ROW_NUMBER.

你的查询也不在同一个地方,所以我不确定比较一个和另一个的成本有什么好处.