Oracle 查询优化器是不是将*** where 子句应用于子查询或视图?

Posted

技术标签:

【中文标题】Oracle 查询优化器是不是将*** where 子句应用于子查询或视图?【英文标题】:Does Oracle Query Optimizer apply top level where clauses to sub queries or views?Oracle 查询优化器是否将*** where 子句应用于子查询或视图? 【发布时间】:2015-10-15 07:46:13 【问题描述】:

Oracle 文档说 Oracle 查询引擎首先执行子查询和视图,然后执行***查询。作为一个自然的结果,Oracle不允许您在子查询中引用***查询的字段值(MSSQL允许这样做)您需要构建一个自给自足的子查询并将结果连接到***查询.

然后是我的问题:Oracle 查询优化器是否在执行期间将***查询的“where 子句”应用于子查询(如果适用)?

假设我有一个单独运行时返回一百万行的子查询或视图,但是当与***查询连接时,由于顶部的连接子句或 where 子句,将只使用其中的 1000 行级查询。 Oracle 是否尝试从子查询中提取所有百万行并在连接期间过滤掉不必要的行,或者 Oracle 查询优化器是否将连接子句或 where 子句从***查询移动到子查询,因此只带来行的子集?

请不要给我一个明显的答案:“这样的查询写得不好,我需要重写我的查询”。有时存在技术或非技术限制,所以我可能无法做到这一点。

我知道 MSSQL 查询优化器会为子查询和视图执行此操作,但由于 Oracle 表示将首先执行子查询,所以我需要询问。 Oracle 查询优化器会这样做吗?

编辑:以下可以用作示例查询。该查询在逻辑上可能不合理,但它确实代表了我的问题的一个示例。子查询返回所有销售额,但***查询仅使用今年的销售额。 Oracle 是计算所有销售额的总和还是仅计算今年的总销售额?

select u.user_fullname, s.date, s.total
from users u
     inner join ( select userID, date, sum(total) as total       
                 from sales
                 group by userID, date
               ) s on s.userID = u.userID
where s.date > '2015-01-01'

【问题讨论】:

Oracle 优化器可以而且确实可以做各种事情,但您的问题似乎更多是关于 SQL 语法的感知限制。然而,如果没有实际的查询,很难回答。 您的第一个声明是错误的,当然您可以在 Oracle 中执行相关子查询。如果已知结果仍然相同,优化器还将尝试将条件推送到派生表/子查询或跨连接。但是您的示例实际上写得不好,因为WHERE-条件将联接的结果从外部更改为内部。查看优化器的计划,看看实际发生了什么。 "Oracle 是计算所有销售额的总和还是仅计算今年的总销售额?"Oracle 实际上会告诉您——只需检查执行计划即可。 【参考方案1】:

Oracle 经常在查询的不同级别之间移动条件。这称为谓词推送。逻辑限制可能会阻止子查询和内联视图引用***项目,但该限制不适用于 Oracle 执行的转换查询。

这个特性在 Oracle 中已经存在很长时间了。文档中有很多对它的引用,甚至还有一些hints 来帮助控制它。 (尽管通常您不需要使用提示。)据我所知,没有任何资料可以准确解释它何时可以工作,但它应该能够在您的示例中工作。在很多情况下,谓词推送是可能的,但未启用,因为优化器认为它没有意义。

这个简单的例子展示了谓词推入的作用:

--Create a simple table.
drop table test1;
create table test1(a number primary key, b number);
insert into test1 select level, 1 from dual connect by level <= 100000;
commit;
begin
    dbms_stats.gather_table_stats(user, 'TEST1');
end;
/

--Create a very slow function.
create or replace function very_slow(p number) return number authid current_user is
begin
    execute immediate 'begin dbms_lock.sleep(1); end;';
    return 1;
end;
/

--This query only takes 1 second.
--Without predicate pushing it would take hours. 
select *
from
(
    select *
    from test1
    where b = very_slow(b)
)
where a = 1;

【讨论】:

以上是关于Oracle 查询优化器是不是将*** where 子句应用于子查询或视图?的主要内容,如果未能解决你的问题,请参考以下文章

Oracle 查询优化的基本准则详解

Oracle查询性能优化

使用 WHERE 子句中的过滤器优化 OUTER JOIN 查询。(查询规划器)

MSSQL里面建索引的问题

oracle group by 性能优化

Oracle数据库查询优化方案