select 语句中未使用的视图中的列会影响 Oracle 中的性能
do columns in a view that aren't used in a select statement affect performance in Oracle
假设我有一个名为 customer_order_info1 的视图。它的代码是这样的。
create or replace view customer_order_info1 as
select
co.order_number,
co.customer_number,
(select address from customer_info ci where ci.customer_number =
co.customer_number) as address
from customer_orders co;
如果我这样做
select
order_number,
customer_number
from customer_order_info1;
它在后台 运行 的代码是什么样的?这样会不会省略视图中的地址栏?
select
co.order_number,
co.customer_number
from customer_orders co
还是后台还是运行地址栏的代码不显示?
第二个问题是,如果视图的代码是这样的。
create or replace view customer_order_info2 as
select
co.order_number,
co.customer_number,
ci.address
from customer_orders co join customer_info ci
on co.customer_number = ci.customer_number;
如果我运行这个
select
order_number,
customer_number
from customer_order_info2;
背景中 运行 的 sql 会是什么样子?
这个?
select
co.order_number,
co.customer_number
from customer_orders co join customer_info ci
on co.customer_number = ci.customer_number;
还是这个?
select
co.order_number,
co.customer_number
from customer_orders co;
如果重要的话,我正在使用 12c。
Oracle 优化器足够智能,可以消除未使用的列,前提是这样做不会影响查询结果(也就是说,如果没有这些列,查询在逻辑上是相同的)。它可以消除整个 tables 并加入,如果它可以保证这样做不会影响结果。它是否会取决于查询和 tables.
上定义的约束
在您的示例中,如果 co.customer_number
是强制性的并且外键以 ci.customer_number
作为其父项,则它可能能够消除 customer_info
,因为这样一来就必须始终只有一行在 customer_info
.
演示:
create table test_parent
( id integer primary key
, parent_name varchar2(30) not null );
create table test_child
( child_id integer primary key
, parent_id references test_parent not null
, child_name varchar2(30) not null );
insert into test_parent values (1, 'Test parent 1');
insert into test_parent values (2, 'Test parent 2');
insert into test_child values (1, 1, 'Test child 1');
insert into test_child values (2, 2, 'Test child 2');
insert into test_child values (3, 1, 'Test child 3');
insert into test_child values (4, 2, 'Test child 4');
commit;
select c.child_id, c.child_name
from test_child c
join test_parent p on p.id = c.parent_id;
执行计划:
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 64 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS STORAGE FULL| TEST_CHILD | 4 | 64 | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
添加 p.parent 名称需要加入,因此计划更改:
select c.child_id, c.child_name, p.parent_name
from test_child c
join test_parent p on p.id = c.parent_id;
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 144 | 6 (17)| 00:00:01 |
| 1 | MERGE JOIN | | 4 | 144 | 6 (17)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| TEST_PARENT | 2 | 34 | 2 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | SYS_C001668561 | 2 | | 1 (0)| 00:00:01 |
|* 4 | SORT JOIN | | 4 | 76 | 4 (25)| 00:00:01 |
| 5 | TABLE ACCESS STORAGE FULL | TEST_CHILD | 4 | 76 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
编辑:标量子查询示例 - 视图对一列使用 table:
create or replace view customer_order_info1 as
select ch.child_id
, ch.child_name
, ( select pa.parent_name from test_parent pa
where pa.id = ch.parent_id ) as parent_name
from test_child ch;
但如果我不在视图查询中使用该列:
select c1.child_id, c1.child_name
from customer_order_info1 c1
我得到这个执行计划:
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 82 | 2460 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS STORAGE FULL| TEST_CHILD | 82 | 2460 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
(无法访问 test_parent
。)
这是 William 针对标量查询示例的回答的附录。这里标量查询包含强制错误(即除以零),但如果我们不 select 该列,我们可以看到一切正常
SQL> create or replace
2 view my_view as
3 select empno,
4 ename,
5 ( select d.deptno / 0 from dept d
6 where d.deptno = e.deptno ) as problem_col
7 from emp e;
View created.
SQL> select * from my_view;
select * from my_view
*
ERROR at line 1:
ORA-01476: divisor is equal to zero
SQL> select empno, ename from my_view;
EMPNO ENAME
---------- ----------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD
7934 MILLER
14 rows selected.
假设我有一个名为 customer_order_info1 的视图。它的代码是这样的。
create or replace view customer_order_info1 as
select
co.order_number,
co.customer_number,
(select address from customer_info ci where ci.customer_number =
co.customer_number) as address
from customer_orders co;
如果我这样做
select
order_number,
customer_number
from customer_order_info1;
它在后台 运行 的代码是什么样的?这样会不会省略视图中的地址栏?
select
co.order_number,
co.customer_number
from customer_orders co
还是后台还是运行地址栏的代码不显示?
第二个问题是,如果视图的代码是这样的。
create or replace view customer_order_info2 as
select
co.order_number,
co.customer_number,
ci.address
from customer_orders co join customer_info ci
on co.customer_number = ci.customer_number;
如果我运行这个
select
order_number,
customer_number
from customer_order_info2;
背景中 运行 的 sql 会是什么样子?
这个?
select
co.order_number,
co.customer_number
from customer_orders co join customer_info ci
on co.customer_number = ci.customer_number;
还是这个?
select
co.order_number,
co.customer_number
from customer_orders co;
如果重要的话,我正在使用 12c。
Oracle 优化器足够智能,可以消除未使用的列,前提是这样做不会影响查询结果(也就是说,如果没有这些列,查询在逻辑上是相同的)。它可以消除整个 tables 并加入,如果它可以保证这样做不会影响结果。它是否会取决于查询和 tables.
上定义的约束在您的示例中,如果 co.customer_number
是强制性的并且外键以 ci.customer_number
作为其父项,则它可能能够消除 customer_info
,因为这样一来就必须始终只有一行在 customer_info
.
演示:
create table test_parent
( id integer primary key
, parent_name varchar2(30) not null );
create table test_child
( child_id integer primary key
, parent_id references test_parent not null
, child_name varchar2(30) not null );
insert into test_parent values (1, 'Test parent 1');
insert into test_parent values (2, 'Test parent 2');
insert into test_child values (1, 1, 'Test child 1');
insert into test_child values (2, 2, 'Test child 2');
insert into test_child values (3, 1, 'Test child 3');
insert into test_child values (4, 2, 'Test child 4');
commit;
select c.child_id, c.child_name
from test_child c
join test_parent p on p.id = c.parent_id;
执行计划:
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 64 | 3 (0)| 00:00:01 |
| 1 | TABLE ACCESS STORAGE FULL| TEST_CHILD | 4 | 64 | 3 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
添加 p.parent 名称需要加入,因此计划更改:
select c.child_id, c.child_name, p.parent_name
from test_child c
join test_parent p on p.id = c.parent_id;
-----------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-----------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 4 | 144 | 6 (17)| 00:00:01 |
| 1 | MERGE JOIN | | 4 | 144 | 6 (17)| 00:00:01 |
| 2 | TABLE ACCESS BY INDEX ROWID| TEST_PARENT | 2 | 34 | 2 (0)| 00:00:01 |
| 3 | INDEX FULL SCAN | SYS_C001668561 | 2 | | 1 (0)| 00:00:01 |
|* 4 | SORT JOIN | | 4 | 76 | 4 (25)| 00:00:01 |
| 5 | TABLE ACCESS STORAGE FULL | TEST_CHILD | 4 | 76 | 3 (0)| 00:00:01 |
-----------------------------------------------------------------------------------------------
编辑:标量子查询示例 - 视图对一列使用 table:
create or replace view customer_order_info1 as
select ch.child_id
, ch.child_name
, ( select pa.parent_name from test_parent pa
where pa.id = ch.parent_id ) as parent_name
from test_child ch;
但如果我不在视图查询中使用该列:
select c1.child_id, c1.child_name
from customer_order_info1 c1
我得到这个执行计划:
----------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
----------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 82 | 2460 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS STORAGE FULL| TEST_CHILD | 82 | 2460 | 2 (0)| 00:00:01 |
----------------------------------------------------------------------------------------
(无法访问 test_parent
。)
这是 William 针对标量查询示例的回答的附录。这里标量查询包含强制错误(即除以零),但如果我们不 select 该列,我们可以看到一切正常
SQL> create or replace
2 view my_view as
3 select empno,
4 ename,
5 ( select d.deptno / 0 from dept d
6 where d.deptno = e.deptno ) as problem_col
7 from emp e;
View created.
SQL> select * from my_view;
select * from my_view
*
ERROR at line 1:
ORA-01476: divisor is equal to zero
SQL> select empno, ename from my_view;
EMPNO ENAME
---------- ----------
7369 SMITH
7499 ALLEN
7521 WARD
7566 JONES
7654 MARTIN
7698 BLAKE
7782 CLARK
7788 SCOTT
7839 KING
7844 TURNER
7876 ADAMS
7900 JAMES
7902 FORD
7934 MILLER
14 rows selected.