我的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工具?
答案 0 :(得分: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
放入子查询中来解决。
答案 1 :(得分:5)
看起来像是一个分页查询。
从这篇ASKTOM文章(约90%页面):
此外,您的查询不在同一个地方,因此我不确定比较其中一个的成本有什么好处。
答案 2 :(得分:1)
您的ORDER BY列是否已编入索引?如果不是那个开始的好地方。
答案 3 :(得分:1)
问题的一部分是“开始”到“结束”跨度有多大以及它们“存在”的位置。 假设你在表中有一百万行,并且你想要行567,890到567,900那么你将不得不忍受这样一个事实:它需要通过整个表,通过id排序几乎所有这些,并确定哪些行属于该范围。
简而言之,这是很多工作,这就是为什么优化器会给它带来高成本的原因。
索引也无法提供帮助。一个索引会给出订单,但充其量,这会让你有一个开始的地方,然后你继续阅读,直到你到达567,900条目。
如果您一次向最终用户显示10个项目,则可能值得从数据库中获取前100名,然后让应用程序将该100个项目分成10个块。
答案 4 :(得分:0)
花更多时间使用EXPLAIN PLAN工具。如果您看到TABLE SCAN,则需要更改查询。
您的查询对我来说毫无意义。查询ROWID似乎在寻找麻烦。该查询中没有关系信息。这是您遇到问题的真实查询,还是您用来说明问题的示例?