视图中未在 select 语句中使用的列会影响 Oracle 中的性能

Posted

技术标签:

【中文标题】视图中未在 select 语句中使用的列会影响 Oracle 中的性能【英文标题】:do columns in a view that aren't used in a select statement affect performance in Oracle 【发布时间】:2018-01-09 18:28:53 【问题描述】:

假设我有一个名为 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。

【问题讨论】:

【参考方案1】:

这是 William 针对标量查询示例的答案的附录。这里标量查询包含一个强制错误(即除以零),但是如果我们不选择列,我们可以看到一切正常

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.

【讨论】:

很好,回答了第一部分。希望我能同时选择你和威廉的,因为两者的结合就是答案。【参考方案2】:

Oracle 优化器足够聪明,可以消除未使用的列,前提是这样做不会影响查询结果(也就是说,如果没有它们,查询在逻辑上是相同的)。如果可以保证这样做不会影响结果,它可以消除整个表和连接。是否这样做取决于查询和表上定义的约束。

在您的示例中,如果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 name 需要加入,所以计划改变:

 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 |
-----------------------------------------------------------------------------------------------

编辑:标量子查询示例 - 视图将表用于一列:

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 无法访问。)

【讨论】:

抱歉,我可能不够清楚。我特别想如何选择语句。 "从 customer_order_info 中选择 order_number、customer_number;"将在我上面描述的两个视图上执行,一个具有内联选择来获取列,另一个具有连接。您的示例与此稍有不同,因为它是表,并且没有动态生成列。 原理是一样的。我构建了最简单的案例,但还会有其他案例。 它可能与我示例中的第二个视图相同,但我无法确定,它与第一个视图不同。 如果您在问题中添加mcve,这会更容易。 经过深思熟虑后,它确实回答了我的问题的第二部分,假设视图工作相同。

以上是关于视图中未在 select 语句中使用的列会影响 Oracle 中的性能的主要内容,如果未能解决你的问题,请参考以下文章

NestedScrollView 中的 GridLayout ,为啥我的列会超出视图空间?

更改列长会以任何方式影响视图吗?

magento 1.8.1中未在销售订单视图上显示原始价格?

在 SQL SELECT 语句中未找到 UDF

如何在SQL Server 2008R2中查找未使用的列

如何在sqlserver中获取表的所有列信息