11

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

SELECT * FROM(
    SELECT
        ...,
        row_number() OVER(ORDER BY ID) rn
    FROM
        ...
) WHERE rn between :start and :end

本质上,是 ORDER BY 部分让事情变慢了。如果我要删除它,EXPLAIN 成本会下降一个数量级(超过 1000 倍)。我试过这个:

SELECT 
    ...
FROM
    ...
WHERE
    rownum between :start and :end

但这并没有给出正确的结果。有什么简单的方法可以加快速度吗?还是我必须花更多时间使用 EXPLAIN 工具?

4

5 回答 5

13

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

此查询将使用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 放入子查询来解决。

于 2009-05-06T14:53:14.197 回答
5

对我来说看起来像是一个分页查询。

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

对于这些分页查询,您需要按唯一的顺序进行排序,以便每次都将 ROW_NUMBER 确定性地分配给行。

此外,您的查询也不尽相同,因此我不确定将一个与另一个的成本进行比较有什么好处。

于 2009-05-05T23:12:05.007 回答
1

您的 ORDER BY 列是否已编入索引?如果不是,那是一个很好的起点。

于 2009-05-05T22:04:30.910 回答
1

部分问题是“开始”到“结束”的跨度有多大以及它们“居住”的地方。假设您在表中有一百万行,并且您想要第 567,890 到 567,900 行,那么您将不得不接受这样一个事实,即它需要遍历整个表,几乎所有这些都按 id 排序,并计算出哪些行属于该范围。

简而言之,这是很多工作,这就是优化器付出高成本的原因。

这也不是索引可以提供多大帮助的东西。索引会给出顺序,但充其量只是给你一个开始的地方,然后你继续阅读,直到你到达第 567,900 个条目。

如果您一次向最终用户展示 10 个项目,那么实际上可能值得从数据库中获取前 100 个项目,然后让应用程序将这 100 个项目分成十个块。

于 2009-05-05T23:25:46.360 回答
0

花更多时间使用 EXPLAIN PLAN 工具。如果您看到 TABLE SCAN,您需要更改您的查询。

你的询问对我来说毫无意义。查询 ROWID 似乎是自找麻烦。该查询中没有关系信息。这是您遇到问题的真实查询还是您为说明问题而编造的示例?

于 2009-05-05T22:53:07.440 回答