当前位置: > 数据库 > Oracle >

Where与Having

时间:2014-12-08 14:24来源:linux.it.net.cn 作者:IT
最近做实验中使用到了group和having,想到where和having之间存在一些共性特点,都是对于结果集合的筛选处理。那么,在获取结果集合相同的情况下,两者的执行计划有什么差异呢?
 
试验一下:
 
首先,准备实验环境,还是选择sys下的dba_objects视图作为数据来源,构建数据表t。
 
//数据表
SQL> create table t as select * from dba_objects where 1=0;
 
Table created
 
//数据生成脚本
declare 
  i number;
begin  
  for i in 1..10 loop
     insert /*+ append */ into t
     select * from dba_objects order by i;
     
     commit;
  end loop;
end;
/
 
//约五十万数据量
SQL> select count(*) from t;
 
  COUNT(*)
----------
    513520
 
//收集统计信息
SQL> exec dbms_stats.gather_table_stats(user,'T',cascade => true);
 
PL/SQL procedure successfully completed
 
语句目的,是获取到SYS下属所有对象的个数。
 
语句1:使用where条件,指定SYS的owner之后,再进行group by操作。
 
 
SQL> select owner,count(*) from t where wner='SYS' group by owner;
 
已用时间:  00: 00: 00.07
//借用autotrace 获取到的执行计划
执行计划
----------------------------------------------------------
Plan hash value: 2913913920
 
-----------------------------------------------------------------------------
| Id  | Operation            | Name | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |      |   228K|  1340K|  1570   (2)| 00:00:19 |
|   1 |  SORT GROUP BY NOSORT|      |   228K|  1340K|  1570   (2)| 00:00:19 |
|*  2 |   TABLE ACCESS FULL  | T    |   228K|  1340K|  1570   (2)| 00:00:19 |
-----------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   2 - filter("OWNER"='SYS')
统计信息
----------------------------------------------------------
         42  recursive calls
          0  db block gets
       7074  consistent gets
          0  physical reads
        764  redo size
        469  bytes sent via SQL*Net to client
        385  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
 
说明:Oracle执行思路比较清晰,首先进行全表扫描,采用filter操作,将owner=’SYS’的条件加入进去,获取到筛选后的结果集合,之后再进行group分组操作。Filter操作在最内层进行。
 
语句2:采用分组方法,再having中,将owner=’sys’作为一个条件进行处理。
 
SQL> select owner,count(*) from t group by owner having wner='SYS';
 
已用时间:  00: 00: 00.17
 
执行计划
----------------------------------------------------------
Plan hash value: 1381620754
 
----------------------------------------------------------------------------
| Id  | Operation           | Name | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT    |      |     1 |     6 |  1642   (6)| 00:00:20 |
|*  1 |  FILTER             |      |       |       |            |          |
|   2 |   HASH GROUP BY     |      |     1 |     6 |  1642   (6)| 00:00:20 |
|   3 |    TABLE ACCESS FULL| T    |   512K|  3001K|  1566   (2)| 00:00:19 |
----------------------------------------------------------------------------
 
Predicate Information (identified by operation id):
---------------------------------------------------
 
   1 - filter("OWNER"='SYS')
 
 
统计信息
----------------------------------------------------------
          1  recursive calls
          0  db block gets
       7070  consistent gets
          0  physical reads
        764  redo size
        469  bytes sent via SQL*Net to client
        385  bytes received via SQL*Net from client
          2  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
          1  rows processed
 
说明:Oracle在处理这个语句时,是先对所有数据进行hash分组操作,作为一个临时数据集合。再将having条件加入进去,作为filter的一部分进行处理。
 
两种方法对比,应该说第一种方法在时间消耗上存在一定程度的优势,同时空间消耗也相对较少。在海量数据的时候,是可以作为优先的选择。
 
 
进一步将,having的最大使用之处,也不是对分组变量条件的设置,而是对分组后聚合操作函数的条件筛选。如果要看对象数据超过1000的owner情况,就需要在having后面加入count(*)>1000的条件了。
 
最后,可能很多读者都会疑惑我写作的目的。其实,这个实验只是想说明一件事,就是Oracle中SQL书写问题。SQL语言是一种比较特殊的程序设计语言,相对于常见的Java/C++等,它本质上是一种描述类语言。对于数据内容,我们在SQL中只是描述出需要数据集合的特性,而不需要制定获取数据的手段方法。
 
对SQL,所有的DBMS都要进行语法验证和解析工作,分析称不同的实际操作原语,执行语句后获取到结果。Oracle也是如此,对执行计划的研究探讨,本质上就是对Oracle是如何获取数据进行的探讨。
 
做同一件事情,描述同一个事物,我们通常有不同的方式手段。同一个数据操作需要,我们可以借助不同的SQL去实现,Oracle也会依据不同的标准生成不同的执行计划。当然,现代DBMS对于SQL语句在解析过程中,是有优化处理,将一些低效率不合理的语句加以替换(通常在exists和in会出现),但是这种自动优化的行为是很有限的。决定一个SQL执行效率的因素很多,但是我们一般可控的两大因素:数据对象的特性(数据表、索引等的设置)以及SQL的书写。数据对象的特性可以说是其中静态的因素,而SQL语句的书写是其中动态的因素。
 
 
“Nerver Treate Your Database as A Blackbox”,是笔者在《Oracle专家编程》中印象深刻的一句话。书写高效的SQL是一件不容易的事情,需要了解很多内部的知识和原理。但首先,我们需要意识到我们的SQL是会造成这些影响,之后再想办法加以优化。
 
 
最后,还是想说下那位高人的名言“Nerver Treate Your Database as A Blackbox”。
(责任编辑:IT)
------分隔线----------------------------