我正在使用Oracle(企业版10g),我有这样的查询:
SELECT * FROM (
SELECT * FROM MyTable
ORDER BY MyColumn
) WHERE rownum <= 10;
MyColumn
已被编入索引,但是,Oracle出于某种原因在切割前10行之前进行全表扫描。因此,对于包含400万条记录的表格,上述时间约为15秒。
现在考虑这个等效的查询:
SELECT MyTable.*
FROM
(SELECT rid
FROM
(SELECT rowid as rid
FROM MyTable
ORDER BY MyColumn
)
WHERE rownum <= 10
)
INNER JOIN MyTable
ON MyTable.rowid = rid
ORDER BY MyColumn;
这里Oracle扫描索引并找到前10个rowid,然后使用嵌套循环按rowid查找10条记录。对于400万张表,这只需不到一秒钟。
请注意,由于特殊原因,我无法使用/*+ FIRST_ROWS(n) */
提示或ROW_NUMBER() OVER (ORDER BY column)
构造。
答案 0 :(得分:1)
如果在您的情况下这是可接受的,添加WHERE ... IS NOT NULL
子句将有助于优化器使用索引,而不是在使用ORDER BY
子句时执行全表扫描:
SELECT * FROM (
SELECT * FROM MyTable
WHERE MyColumn IS NOT NULL
-- ^^^^^^^^^^^^^^^^^^^^
ORDER BY MyColumn
) WHERE rownum <= 10;
理性是Oracle不会在索引中存储NULL
值。在您最初编写查询时,优化器决定进行全表扫描,就好像少于10个非NULL值一样,它应该检索一些&#34; NULL行&#34;到&#34;填写&#34;剩下的行。显然,如果索引包含足够的行,则首先检查它是不够聪明的......
添加WHERE MyColumn IS NOT NULL
后,您会通知优化工具,在任何情况下,您都不希望NULL
中有MyColumn
行。因此,它可以盲目地使用索引,而不必担心NULL
中MyColumn
的假设行。
出于同样的原因,将ORDER BY
列声明为NOT NULL
会阻止优化程序执行全表扫描。因此,如果您可以更改架构,则更清晰的选项是:
ALTER TABLE MyTable MODIFY (MyColumn NOT NULL);
请参阅http://sqlfiddle.com/#!4/e3616/1进行各种比较(点击查看执行计划)