Where与Having

最近做实验中使用到了grouphaving,想到wherehaving之间存在一些共性特点,都是对于结果集合的筛选处理。那么,在获取结果集合相同的情况下,两者的执行计划有什么差异呢?

 

试验一下:

 

首先,准备实验环境,还是选择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条件,指定SYSowner之后,再进行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的最大使用之处,也不是对分组变量条件的设置,而是对分组后聚合操作函数的条件筛选。如果要看对象数据超过1000owner情况,就需要在having后面加入count(*)>1000的条件了。

 

最后,可能很多读者都会疑惑我写作的目的。其实,这个实验只是想说明一件事,就是OracleSQL书写问题。SQL语言是一种比较特殊的程序设计语言,相对于常见的Java/C++等,它本质上是一种描述类语言。对于数据内容,我们在SQL中只是描述出需要数据集合的特性,而不需要制定获取数据的手段方法。

 

SQL,所有的DBMS都要进行语法验证和解析工作,分析称不同的实际操作原语,执行语句后获取到结果。Oracle也是如此,对执行计划的研究探讨,本质上就是对Oracle是如何获取数据进行的探讨。

 

做同一件事情,描述同一个事物,我们通常有不同的方式手段。同一个数据操作需要,我们可以借助不同的SQL去实现,Oracle也会依据不同的标准生成不同的执行计划。当然,现代DBMS对于SQL语句在解析过程中,是有优化处理,将一些低效率不合理的语句加以替换(通常在existsin会出现),但是这种自动优化的行为是很有限的。决定一个SQL执行效率的因素很多,但是我们一般可控的两大因素:数据对象的特性(数据表、索引等的设置)以及SQL的书写。数据对象的特性可以说是其中静态的因素,而SQL语句的书写是其中动态的因素。

 

 

Nerver Treate Your Database as A Blackbox”,是笔者在《Oracle专家编程》中印象深刻的一句话。书写高效的SQL是一件不容易的事情,需要了解很多内部的知识和原理。但首先,我们需要意识到我们的SQL是会造成这些影响,之后再想办法加以优化。

 

 

最后,还是想说下那位高人的名言“Nerver Treate Your Database as A Blackbox”。

 

请使用浏览器的分享功能分享到微信等