Mat*_* M. 3 oracle oracle-11g execution-plan
我一直致力于从我的数据库 (11g) 中的表中获取分页结果。虽然我有一个有效的查询(即结果是正确的),但它的性能不如我希望的那样好,我正在努力提高它的效率(估计仅在该查询上每秒调用 60 次)。
所以首先,我阅读了在 Oracle 中计算页数的有效方法是什么?,并且文章也指出了这一点,但不幸的是,它根本没有讨论所提出的查询的执行计划(我会在星期一看到它们)。
这是我提出的查询(表分区part_code是日期范围):
select <all-columns> from (
select rownum as rnum, <all-columns> from (
select /*+index (my_table index)*/ <all-columns> from my_table
where part_code in (?, ?, ?)
and date between ? and ?
and type in (?, ?, ?)
and func_col1 = ?
/*potentially: and func_col2 = ? and func_col3 = ? ... */
order by date, type, id
)
) where rnum between M and N; /* N-M ~= 30 */
Run Code Online (Sandbox Code Playgroud)
注意:大多数查询将使用单个func_xxx过滤器进行,我认为 M 会很小......但不能保证,理论上 M 可以达到 9999。
注意:〜72个分区中总,但只有〜39活性至多,〜300000个的不同的值func_col1(但有一些具有〜50000行和一些具有1行),〜10倍的值type,id是唯一的(通过一个序列生成)。
它确实有效,但执行计划中有一个令人讨厌的惊喜:第二个查询(with rownum as rnum)已完全执行,在分页开始之前从数据库中提取了大约 50,000 行。从数据库中提取了大约 50,000 行以返回其中约 30 个似乎特别低效。
在执行计划中,这显示为:
View
\_ Filter (rnum)
\_ View <= here comes the trouble
\_ Sort
\_ ...
Run Code Online (Sandbox Code Playgroud)
如有必要,我可以在表上创建索引,并且可以转换现有的分区索引 ( part_code, func_col1, date, type, id)。索引完全符合我的order by条款所要求的顺序;但这似乎还不够(并且删除 order by 子句并没有让我更接近)。
有没有办法防止视图的“具体化”,并让 Oracle 在外部查询需要更多数据时动态构建它(即,转向内部查询的延迟评估)?
我相信您应该制定一个避免任何实际排序操作的计划,并尽快“停止”。
为了避免排序(并“具体化”内部视图),您的排序顺序必须与索引列完全匹配,或者您的 where 子句必须仅在所有前导列上严格等于。否则将需要对子集进行排序,这将需要评估整个内部视图。
这是一个带有假设foo表的示例:
create table foo (a number, b number, c varchar(100));
-- fill with dummy data
exec dbms_stats.gather_table_stats(ownname => user, tabname => 'FOO');
create index foo_ix on foo(a, b, c);
Run Code Online (Sandbox Code Playgroud)
与您的选择最接近的简单等效项:
select * from (
select rownum r, i.* from (
select a, b, c
from foo
where a in (3,4) and b = 3
order by c
) i
) where r between 5 and 10;
Run Code Online (Sandbox Code Playgroud)
解释计划不好:
--------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 163 | 14833 | 5 (20)| 00:00:01 |
|* 1 | VIEW | | 163 | 14833 | 5 (20)| 00:00:01 |
| 2 | COUNT | | | | | |
| 3 | VIEW | | 163 | 12714 | 5 (20)| 00:00:01 |
| 4 | SORT ORDER BY | | 163 | 9291 | 5 (20)| 00:00:01 |
| 5 | INLIST ITERATOR | | | | | |
|* 6 | INDEX RANGE SCAN| FOO_IX | 163 | 9291 | 4 (0)| 00:00:01 |
--------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("R"<=10 AND "R">=5)
6 - access(("A"=3 OR "A"=4) AND "B"=3)
Run Code Online (Sandbox Code Playgroud)
计数为时已晚,并且在这种情况下实际上(我认为)并不是“停止工作”。
将索引列添加到订单中(可能不符合您的要求,抱歉):
select * from (
select rownum r, i.* from (
select a, b, c
from foo
where a in (3,4) and b = 3
order by a, b, c
) i
) where r between 5 and 10;
Run Code Online (Sandbox Code Playgroud)
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 163 | 14833 | 4 (0)| 00:00:01 |
|* 1 | VIEW | | 163 | 14833 | 4 (0)| 00:00:01 |
| 2 | COUNT | | | | | |
| 3 | VIEW | | 163 | 12714 | 4 (0)| 00:00:01 |
| 4 | INLIST ITERATOR | | | | | |
|* 5 | INDEX RANGE SCAN| FOO_IX | 163 | 9291 | 4 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("R"<=10 AND "R">=5)
5 - access(("A"=3 OR "A"=4) AND "B"=3)
Run Code Online (Sandbox Code Playgroud)
至少我们摆脱了那种。现在让我们试着“停下来”:
select * from (
select rownum r, i.* from (
select a, b, c
from foo
where a in (3,4) and b = 3
order by a, b, c
) i where rownum < 10
) where r > 5;
Run Code Online (Sandbox Code Playgroud)
-------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 9 | 819 | 4 (0)| 00:00:01 |
|* 1 | VIEW | | 9 | 819 | 4 (0)| 00:00:01 |
|* 2 | COUNT STOPKEY | | | | | |
| 3 | VIEW | | 9 | 702 | 4 (0)| 00:00:01 |
| 4 | INLIST ITERATOR | | | | | |
|* 5 | INDEX RANGE SCAN| FOO_IX | 9 | 513 | 4 (0)| 00:00:01 |
-------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - filter("R">5)
2 - filter(ROWNUM<10)
5 - access(("A"=3 OR "A"=4) AND "B"=3)
Run Code Online (Sandbox Code Playgroud)
这对于您的查询来说可能就足够了:注意count stopkey(rownum <魔术,不会between在我所看到的内容中起作用) 和“行”列 - 内部扫描不必费心在找到后获取更多行N。
有了上面的内容,您仍然会N从表中读取行。
如果所有搜索条件都被索引,您可以限制:执行上述过滤但仅从ROWID每个匹配项中检索而不是所有列,然后通过ROWID.
select * from foo where rowid in (
select rid from (
select rownum r, i.* from (
select rowid rid
from foo
where a in (3,4) and b = 3
order by a, b, c
) i where rownum < 10
) where r > 5
);
Run Code Online (Sandbox Code Playgroud)
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 78 | 6 (17)| 00:00:01 |
| 1 | NESTED LOOPS | | 1 | 78 | 6 (17)| 00:00:01 |
| 2 | VIEW | VW_NSO_1 | 9 | 108 | 4 (0)| 00:00:01 |
| 3 | HASH UNIQUE | | 1 | 225 | | |
|* 4 | VIEW | | 9 | 225 | 4 (0)| 00:00:01 |
|* 5 | COUNT STOPKEY | | | | | |
| 6 | VIEW | | 9 | 108 | 4 (0)| 00:00:01 |
| 7 | INLIST ITERATOR | | | | | |
|* 8 | INDEX RANGE SCAN | FOO_IX | 9 | 594 | 4 (0)| 00:00:01 |
| 9 | TABLE ACCESS BY USER ROWID| FOO | 1 | 66 | 1 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
4 - filter("R">5)
5 - filter(ROWNUM<10)
8 - access(("A"=3 OR "A"=4) AND "B"=3)
Run Code Online (Sandbox Code Playgroud)
如果任何搜索字段不在索引中,这将不起作用。并确保您使用真实数据和通常的搜索条件对其进行跟踪,以查看它是否有实质性影响 - 实际上可能更糟,尤其是对于低值M(这可能是您想要最快的情况)或高值N-M.
| 归档时间: |
|
| 查看次数: |
2889 次 |
| 最近记录: |