我的目的是获得客户的分页结果集。我正在使用此算法,来自Tom:
select * from (
select /*+ FIRST_ROWS(20) */ FIRST_NAME, ROW_NUMBER() over (order by FIRST_NAME) RN
from CUSTOMER C
)
where RN between 1 and 20
order by RN;
我还在“CUSTOMER”列上定义了一个索引。“FIRST_NAME”:
CREATE INDEX CUSTOMER_FIRST_NAME_TEST ON CUSTOMER (FIRST_NAME ASC);
查询返回预期的结果集,但是从解释计划中我注意到没有使用索引:
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 15467 | 679K| 157 (3)| 00:00:02 |
| 1 | SORT ORDER BY | | 15467 | 679K| 157 (3)| 00:00:02 |
|* 2 | VIEW | | 15467 | 679K| 155 (2)| 00:00:02 |
|* 3 | WINDOW SORT PUSHED RANK| | 15467 | 151K| 155 (2)| 00:00:02 |
| 4 | TABLE ACCESS FULL | CUSTOMER | 15467 | 151K| 154 (1)| 00:00:02 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("RN">=1 AND "RN"<=20)
3 - filter(ROW_NUMBER() OVER ( ORDER BY "FIRST_NAME")<=20)
我正在使用Oracle 11g。由于我只查询索引列排序的前20行,我希望使用索引。
为什么Oracle优化器会忽略索引?我认为分页算法有问题,但我无法弄清楚是什么。
感谢。
答案 0 :(得分:22)
您的FIRST_NAME专栏很可能是可以为空的。
SQL> create table customer (first_name varchar2(20), last_name varchar2(20));
Table created.
SQL> insert into customer select dbms_random.string('U', 20), dbms_random.string('U', 20) from dual connect by level <= 100000;
100000 rows created.
SQL> create index c on customer(first_name);
Index created.
SQL> explain plan for select * from (
2 select /*+ FIRST_ROWS(20) */ FIRST_NAME, ROW_NUMBER() over (order by FIRST_NAME) RN
3 from CUSTOMER C
4 )
5 where RN between 1 and 20
6 order by RN;
Explained.
SQL> @explain ""
Plan hash value: 1474094583
----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 117K| 2856K| | 1592 (1)| 00:00:20 |
| 1 | SORT ORDER BY | | 117K| 2856K| 4152K| 1592 (1)| 00:00:20 |
|* 2 | VIEW | | 117K| 2856K| | 744 (2)| 00:00:09 |
|* 3 | WINDOW SORT PUSHED RANK| | 117K| 1371K| 2304K| 744 (2)| 00:00:09 |
| 4 | TABLE ACCESS FULL | CUSTOMER | 117K| 1371K| | 205 (1)| 00:00:03 |
----------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("RN">=1 AND "RN"<=20)
3 - filter(ROW_NUMBER() OVER ( ORDER BY "FIRST_NAME")<=20)
Note
-----
- dynamic sampling used for this statement (level=2)
21 rows selected.
SQL> alter table customer modify first_name not null;
Table altered.
SQL> explain plan for select * from (
2 select /*+ FIRST_ROWS(20) */ FIRST_NAME, ROW_NUMBER() over (order by FIRST_NAME) RN
3 from CUSTOMER C
4 )
5 where RN between 1 and 20
6 order by RN;
Explained.
SQL> @explain ""
Plan hash value: 1725028138
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 117K| 2856K| | 850 (1)| 00:00:11 |
| 1 | SORT ORDER BY | | 117K| 2856K| 4152K| 850 (1)| 00:00:11 |
|* 2 | VIEW | | 117K| 2856K| | 2 (0)| 00:00:01 |
|* 3 | WINDOW NOSORT STOPKEY| | 117K| 1371K| | 2 (0)| 00:00:01 |
| 4 | INDEX FULL SCAN | C | 117K| 1371K| | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("RN">=1 AND "RN"<=20)
3 - filter(ROW_NUMBER() OVER ( ORDER BY "FIRST_NAME")<=20)
Note
-----
- dynamic sampling used for this statement (level=2)
21 rows selected.
SQL>
在那里添加一个NOT NULL来解决它。
SQL> explain plan for select * from (
2 select /*+ FIRST_ROWS(20) */ FIRST_NAME, ROW_NUMBER() over (order by FIRST_NAME) RN
3 from CUSTOMER C
4 where first_name is not null
5 )
6 where RN between 1 and 20
7 order by RN;
Explained.
SQL> @explain ""
Plan hash value: 1725028138
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 117K| 2856K| | 850 (1)| 00:00:11 |
| 1 | SORT ORDER BY | | 117K| 2856K| 4152K| 850 (1)| 00:00:11 |
|* 2 | VIEW | | 117K| 2856K| | 2 (0)| 00:00:01 |
|* 3 | WINDOW NOSORT STOPKEY| | 117K| 1371K| | 2 (0)| 00:00:01 |
|* 4 | INDEX FULL SCAN | C | 117K| 1371K| | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - filter("RN">=1 AND "RN"<=20)
3 - filter(ROW_NUMBER() OVER ( ORDER BY "FIRST_NAME")<=20)
4 - filter("FIRST_NAME" IS NOT NULL)
Note
-----
- dynamic sampling used for this statement (level=2)
22 rows selected.
SQL>
答案 1 :(得分:0)
您要查询的列数多于first_name
。 first_name
上的索引只包含first_name
列和对表的引用。因此,要检索其他列,Oracle必须对表格本身执行每行的查找。如果大多数数据库无法保证低记录数,则会尽量避免这种情况。
数据库通常不够智能,无法了解where
列对row_number
列的影响。但是,您的提示/*+ FIRST_ROWS(20) */
可能已经成功了。
表可能很小,因此Oracle希望表扫描比查找更便宜,即使只有20行。