Sql 通过使用具有内联提示的命名子查询替换子查询进行优化
让我们有这两张桌子:Sql 通过使用具有内联提示的命名子查询替换子查询进行优化,sql,oracle,oracle11g,subquery,query-optimization,Sql,Oracle,Oracle11g,Subquery,Query Optimization,让我们有这两张桌子: create table table_x( x_id varchar2(100) primary key ); create table table_y( x_id varchar2(100) references table_x(x_id), stream varchar2(10), val_a number, val_b number ); create index table_y_idx on table_y (x_id, stream);
create table table_x(
x_id varchar2(100) primary key
);
create table table_y(
x_id varchar2(100) references table_x(x_id),
stream varchar2(10),
val_a number,
val_b number
);
create index table_y_idx on table_y (x_id, stream);
假设每个表中有数百万行,table_y
每个x_id
包含0到10行
以下示例中的查询按filtersubstr(x_id,2,1)=“B”
返回200行
需要优化查询:
QUERY 1
select
x.x_id,
y.val_a,
y.val_b
from table_x x
left join (select
x_id,
max(val_a) KEEP (DENSE_RANK FIRST ORDER BY stream) as val_a,
max(val_b) KEEP (DENSE_RANK FIRST ORDER BY stream) as val_b
from table_y
group by x_id
) y on x.x_id = y.x_id
where substr(x.x_id, 2, 1) = 'B'; -- intentionally not use the primary key filter
------
PLAN 1
----------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
----------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10000 | 2400000 | 22698 | 00:04:33 |
| * 1 | HASH JOIN OUTER | | 10000 | 2400000 | 22698 | 00:04:33 |
| * 2 | TABLE ACCESS FULL | TABLE_X | 10000 | 120000 | 669 | 00:00:09 |
| 3 | VIEW | | 10692 | 2437776 | 22029 | 00:04:25 |
| 4 | SORT GROUP BY | | 10692 | 245916 | 22029 | 00:04:25 |
| 5 | TABLE ACCESS FULL | TABLE_Y | 1069200 | 24591600 | 19359 | 00:03:53 |
----------------------------------------------------------------------------------
* 1 - access("X"."X_ID"="Y"."X_ID"(+))
* 2 - filter(SUBSTR("X"."X_ID", 2, 1)='B')
有一种重要的优化方法,因此query2
返回行的速度比query1
快2-3倍。INLINE
提示非常重要,因为没有它,第二个提示的执行速度与第一个提示一样慢
QUERY 2
with
table_y_total as (
select --+ INLINE
x_id,
max(val_a) KEEP (DENSE_RANK FIRST ORDER BY stream) as val_a,
max(val_b) KEEP (DENSE_RANK FIRST ORDER BY stream) as val_b
from table_y
group by x_id
)
select
x.x_id,
(select val_a from table_y_total y where y.x_id = x.x_id) as val_a,
(select val_b from table_y_total y where y.x_id = x.x_id) as val_b
from table_x x
where substr(x.x_id, 2, 1) = 'B';
------
PLAN 2
-----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost | Time |
-----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 10000 | 120000 | 669 | 00:00:09 |
| 1 | SORT GROUP BY NOSORT | | 1 | 19 | 103 | 00:00:02 |
| 2 | TABLE ACCESS BY INDEX ROWID | TABLE_Y | 100 | 1900 | 103 | 00:00:02 |
| * 3 | INDEX RANGE SCAN | TABLE_Y_IDX | 100 | | 3 | 00:00:01 |
| 4 | SORT GROUP BY NOSORT | | 1 | 20 | 103 | 00:00:02 |
| 5 | TABLE ACCESS BY INDEX ROWID | TABLE_Y | 100 | 2000 | 103 | 00:00:02 |
| * 6 | INDEX RANGE SCAN | TABLE_Y_IDX | 100 | | 3 | 00:00:01 |
| * 7 | TABLE ACCESS FULL | TABLE_X | 10000 | 120000 | 669 | 00:00:09 |
-----------------------------------------------------------------------------------------
* 3 - access("X_ID"=:B1)
* 6 - access("X_ID"=:B1)
* 7 - filter(SUBSTR("X"."X_ID", 2, 1)='B')
由于第一个查询使用较少的代码重复,我宁愿保留它
是否有提示或其他技巧可以同时满足以下条件
- 保留第一个查询代码(
)query1
- 强制优化器使用第二个计划(
)计划2
- 也许您的代码过于简化了,但这不是您想要的吗:
select y.x_id,
max(y.val_a) KEEP (DENSE_RANK FIRST ORDER BY stream) as val_a,
max(y.val_b) KEEP (DENSE_RANK FIRST ORDER BY stream) as val_b
from table_y y
where substr(y.x_id, 2, 1) = 'B'
group by x_id;
我不认为表x的连接是不必要的,因为您已经提出了这个问题。使用索引提示
select /*+index(index_name)*/ from table
由于对
表_x
的完全扫描是计划中最便宜的部分,因此有一种方法可以在加入表_y
之前对其进行过滤。尽管优化器决定在默认情况下对表y
使用完全扫描,但使用索引(y)
进行提示有助于将时间减少到查询2
的110%
with
table_x_filtered as (
select x_id
from table_x
where substr(x_id, 2, 1) = 'B'
)
select /*+ index(y table_y_idx) */
x.x_id,
max(val_a) KEEP (DENSE_RANK FIRST ORDER BY stream) as val_a,
max(val_b) KEEP (DENSE_RANK FIRST ORDER BY stream) as val_b
from table_x_filtered x
left join table_y y on y.x_id = x.x_id
group by x.x_id;
我刚发现我打错了。该查询应该使用
左外部联接
。对于这个错误,我很抱歉,我将编辑我的帖子。请在你的答案中添加解释*