经常会在执行计划中看到很奇怪的"FILTER"操作,然后看对应的执行信息是"filter(NULL IS NOT NULL)".  其实这是优化器非常聪明的“短路”操作。

比如下面的这个执行计划,(尤其是从统计信息中可以看到logical/physical reads都是0)

(注:在Ask Tom: On Constraints, Metadata, and Truth (http://www.oracle.com/technetwork/issue-archive/2011/11-mar/o21asktom-312223.html) 有提到)

Execution Plan
----------------------------------------------------------
Plan hash value: 3049830378 ---------------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 2 | 58 | 2 (100)| 00:00:01 |
| 1 | VIEW | V_LO_SEC_USER_US | 2 | 58 | 2 (100)| 00:00:01 |
| 2 | SORT UNIQUE | | 2 | 38 | 2 (100)| 00:00:01 |
| 3 | UNION-ALL | | | | | |
|* 4 | FILTER | | | | | |
|* 5 | HASH JOIN | | 5 | 160 | 3 (34)| 00:00:01 |
| 6 | VIEW | | 3 | 78 | 1 (0)| 00:00:01 |
|* 7 | CONNECT BY WITHOUT FILTERING| | | | | |
| 8 | INDEX FULL SCAN | PK_LO_SEC_USER_SET_LINKS | 3 | 18 | 1 (0)| 00:00:01 |
| 9 | INDEX FULL SCAN | PK_LO_SEC_USER_US_LINKS | 8 | 48 | 1 (0)| 00:00:01 |
|* 10 | FILTER | | | | | |
| 11 | INDEX FULL SCAN | PK_LO_SEC_USER_US_LINKS | 8 | 48 | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id):
--------------------------------------------------- 4 - filter(NULL IS NOT NULL)
5 - access("US"."CHILD_USER_SET_ID"="USL"."USER_SET_ID")
7 - access("CHILD_USER_SET_ID"=PRIOR "PARENT_USER_SET_ID")
10 - filter(NULL IS NOT NULL) Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
0 consistent gets
0 physical reads
0 redo size
467 bytes sent via SQL*Net to client
493 bytes received via SQL*Net from client
1 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
0 rows processed

这个是很赞的。

不过今天在看一条SQL执行计划的时候,却发现有点异常。

SQL是这样的

 select * from V_LO_SEC_BU where is_org=1;

View的定义如下:

CREATE OR REPLACE FORCE VIEW V_LO_SEC_BU
(
IS_ORG,
BU_ID,
CHILD_BU_ID,
IS_DIRECT
)
AS
SELECT /* IS_ORG = 1 only return organzational tree, IS_ORG=0 return all the tree*/
1 AS is_org,
CONNECT_BY_ROOT parent_bu_id AS bu_id,
child_bu_id AS child_bu_id,
CASE WHEN level > 1 THEN 0 ELSE 1 END AS is_direct
FROM (SELECT * FROM lo_sec_bu_links WHERE is_organization = 'Y')
CONNECT BY PRIOR child_bu_id = parent_bu_id
UNION ALL
SELECT 0 AS is_org,
CONNECT_BY_ROOT parent_bu_id AS bu_id,
child_bu_id AS child_bu_id,
CASE WHEN level > 1 THEN 0 ELSE 1 END AS is_direct
FROM lo_sec_bu_links
CONNECT BY PRIOR child_bu_id = parent_bu_id;

这个View很简单了,就是个两部分结果的合集。UNION 之前的是 is_org=1的数据, UNION之后的是is_org=0的数据。上面的SQL是只想得到UNION之前的数据,按道理是SQL优化器是可以知道只要执行UNION之前的SQL就好了,UNION下面的SQL可以直接短路掉。但是执行计划看来不是这样的,

Execution Plan
----------------------------------------------------------
Plan hash value: 1778311211 ------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 25 | 800 | 4 (0)| 00:00:01 |
| 1 | VIEW | V_LO_SEC_BU | 25 | 800 | 4 (0)| 00:00:01 |
| 2 | UNION-ALL | | | | | |
|* 3 | FILTER | | | | | |
|* 4 | CONNECT BY WITHOUT FILTERING| | | | | |
|* 5 | TABLE ACCESS FULL | LO_SEC_BU_LINKS | 12 | 96 | 3 (0)| 00:00:01 |
|* 6 | FILTER | | | | | |
|* 7 | CONNECT BY WITHOUT FILTERING| | | | | |
| 8 | INDEX FULL SCAN | PK_LO_SEC_BU_LINKS | 13 | 78 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------ Predicate Information (identified by operation id):
--------------------------------------------------- - filter(=)
4 - access("LO_SEC_BU_LINKS"."PARENT_BU_ID"=PRIOR "LO_SEC_BU_LINKS"."CHILD_BU_ID")
5 - filter("IS_ORGANIZATION"='Y')
- filter(=)
7 - access("PARENT_BU_ID"=PRIOR "CHILD_BU_ID") Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
7 consistent gets
0 physical reads
0 redo size
1193 bytes sent via SQL*Net to client
515 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
2 sorts (memory)
0 sorts (disk)
26 rows processed

注意第6步操作是 filter(0=1), 而不是filter(NULL IS NOT NULL)! 虽然0=1和 NULL IS NOT NULL 都是FALSE, 但是filter (0=1)却只是个简单的对结果进行过滤的操作,而不是短路(i.e. Filter的子操作(step7和8)都不执行)!

那么怎么帮助SQL优化器知道这是个可以“短路”的操作呢。试着改下这个View, 如下 -

CREATE OR REPLACE FORCE VIEW V_LO_SEC_BU
(
IS_ORG,
BU_ID,
CHILD_BU_ID,
IS_DIRECT
)
AS
SELECT is_org,
bu_id,
child_bu_id,
is_direct
FROM
(
SELECT /* IS_ORG = 1 only return organzational tree, IS_ORG=0 return all the tree*/
1 AS is_org,
CONNECT_BY_ROOT parent_bu_id AS bu_id,
child_bu_id AS child_bu_id,
CASE WHEN level > 1 THEN 0 ELSE 1 END AS is_direct
FROM (SELECT * FROM lo_sec_bu_links WHERE is_organization = 'Y')
CONNECT BY PRIOR child_bu_id = parent_bu_id
)
WHERE is_org = 1
UNION ALL
SELECT is_org,
bu_id,
child_bu_id,
is_direct
FROM
(
SELECT 0 AS is_org,
CONNECT_BY_ROOT parent_bu_id AS bu_id,
child_bu_id AS child_bu_id,
CASE WHEN level > 1 THEN 0 ELSE 1 END AS is_direct
FROM lo_sec_bu_links
CONNECT BY PRIOR child_bu_id = parent_bu_id
)
WHERE is_org = 0
;

再次执行下上面的SQL,执行计划如下,

Execution Plan
----------------------------------------------------------
Plan hash value: 959389615 -------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 13 | 416 | 3 (0)| 00:00:01 |
| 1 | VIEW | V_LO_SEC_BU | 13 | 416 | 3 (0)| 00:00:01 |
| 2 | UNION-ALL | | | | | |
|* 3 | VIEW | | 12 | 384 | 3 (0)| 00:00:01 |
|* 4 | CONNECT BY WITHOUT FILTERING | | | | | |
|* 5 | TABLE ACCESS FULL | LO_SEC_BU_LINKS | 12 | 96 | 3 (0)| 00:00:01 |
|* 6 | FILTER | | | | | |
|* 7 | VIEW | | 13 | 403 | 1 (0)| 00:00:01 |
|* 8 | CONNECT BY WITHOUT FILTERING| | | | | |
| 9 | INDEX FULL SCAN | PK_LO_SEC_BU_LINKS | 13 | 78 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id):
--------------------------------------------------- 3 - filter("IS_ORG"=1)
4 - access("LO_SEC_BU_LINKS"."PARENT_BU_ID"=PRIOR "LO_SEC_BU_LINKS"."CHILD_BU_ID")
5 - filter("IS_ORGANIZATION"='Y')
6 - filter(NULL IS NOT NULL)
7 - filter("IS_ORG"=0)
8 - access("PARENT_BU_ID"=PRIOR "CHILD_BU_ID") Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
6 consistent gets
0 physical reads
0 redo size
1193 bytes sent via SQL*Net to client
515 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
26 rows processed

这次就出现了filter(NULL IS NOT NULL), 而且从consistent gets (由7下降为6)可以看出UNION之后的部分是没有执行的。如果SQL改成读取is_org=0效果会更加明显。

Execution Plan
----------------------------------------------------------
Plan hash value: 4072402958 -------------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 14 | 448 | 1 (0)| 00:00:01 |
| 1 | VIEW | V_LO_SEC_BU | 14 | 448 | 1 (0)| 00:00:01 |
| 2 | UNION-ALL | | | | | |
|* 3 | FILTER | | | | | |
|* 4 | VIEW | | 12 | 384 | 3 (0)| 00:00:01 |
|* 5 | CONNECT BY WITHOUT FILTERING| | | | | |
|* 6 | TABLE ACCESS FULL | LO_SEC_BU_LINKS | 12 | 96 | 3 (0)| 00:00:01 |
|* 7 | VIEW | | 13 | 403 | 1 (0)| 00:00:01 |
|* 8 | CONNECT BY WITHOUT FILTERING | | | | | |
| 9 | INDEX FULL SCAN | PK_LO_SEC_BU_LINKS | 13 | 78 | 1 (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------- Predicate Information (identified by operation id):
--------------------------------------------------- 3 - filter(NULL IS NOT NULL)
4 - filter("IS_ORG"=1)
5 - access("LO_SEC_BU_LINKS"."PARENT_BU_ID"=PRIOR "LO_SEC_BU_LINKS"."CHILD_BU_ID")
6 - filter("IS_ORGANIZATION"='Y')
7 - filter("IS_ORG"=0)
8 - access("PARENT_BU_ID"=PRIOR "CHILD_BU_ID") Statistics
----------------------------------------------------------
0 recursive calls
0 db block gets
1 consistent gets
0 physical reads
0 redo size
1215 bytes sent via SQL*Net to client
515 bytes received via SQL*Net from client
3 SQL*Net roundtrips to/from client
1 sorts (memory)
0 sorts (disk)
28 rows processed

这次发现consistent gets 为1, 正好印证了is_org=1短路了(consistent gets 减少了1) UNION之后的那部分的操作。

了不起的 “filter(NULL IS NOT NULL)”的更多相关文章

  1. com.opensymphony.xwork2.ognl.OgnlValueStack] - target is null for setProperty(null, "emailTypeNo", [Ljava.lang.String;@6f205e]

    情况1,查询结果未转换为与前台交互的实体类DTO 实体类:EmailTypeDto package com.manage.email.dto; public class EmailTypeDto { ...

  2. ognl.OgnlException: target is null for setProperty(null, "emailTypeNo", [Ljava.lang.String;@1513fd0)

    [com.opensymphony.xwork2.ognl.OgnlValueStack] - Error setting expression 'emaiTypeDto.emailTypeNo' w ...

  3. IOS开发遇到(null)与<null>轻松处理

    在ios开发中不可避免的我们会遇到服务器返回的值有空值,但是如果是nil也就算了还可能得到(null)以及<null>的返回值,该如何处理呢?(当然有的字典转模型中已处理,可以通过遍历等) ...

  4. SQL语句中=null和is null

    平时经常会遇到这两种写法:IS NOT NULL与!=NULL.也经常会遇到数据库有符合条件!=NULL的数据,但是返回为空集合.实际上,是由于对二者使用区别理解不透彻. 默认情况下,推荐使用 IS ...

  5. mysql探究之null与not null

    相信很多用了mysql很久的人,对这两个字段属性的概念还不是很清楚,一般会有以下疑问: 1.我字段类型是not null,为什么我可以插入空值 2.为毛not null的效率比null高 3.判断字段 ...

  6. 【MySQL】探究之null与not null

    相信很多用了mysql很久的人,对这两个字段属性的概念还不是很清楚,一般会有以下疑问: 我字段类型是not null,为什么我可以插入空值 为毛not null的效率比null高 判断字段不为空的时候 ...

  7. IOS开发中(null)与<null>的处理

    不小心在开发过程中,得到了(null)以及<null>的返回值,找了好长时间只找到了一个关于<null>的. 由于要根据返回值进行判断,做出必要反应,因此必须知道返回值所代表的 ...

  8. 数据库中is null(is not null)与=null(!=null)的区别

    在标准SQL语言(ANIS SQL)SQL-92规定的Null值的比较取值结果都为False,既Null=Null取值也是False.NULL在这里是一种未知值,千变万化的变量,不是“空”这一个定值! ...

  9. 原!! java直接打印一个对象时,并不是直接调用该类的toString方法 ,而是会先判断是否为null,非null才会调用toString方法

    网上看了好多java直接打印一个对象时,直接调用该类的toString方法 . 但是: Object obj=null; System.out.println(obj);//没有报错 System.o ...

随机推荐

  1. QR分解与最小二乘

    主要内容: 1.QR分解定义 2.QR分解求法 3.QR分解与最小二乘 4.Matlab实现   一.QR分解 R分解法是三种将矩阵分解的方式之一.这种方式,把矩阵分解成一个正交矩阵与一个上三角矩阵的 ...

  2. 消息队列的使用场景(转载c)

    作者:ScienJus链接:https://www.zhihu.com/question/34243607/answer/58314162来源:知乎著作权归作者所有.商业转载请联系作者获得授权,非商业 ...

  3. TQ2440 LCD试验失败经验教训

    试验环境:TQ2440开发板(配套TQ4.3寸屏) 试验参考书目:<ARM处理器裸机开发实战--机制而非策略>(以下简称<裸机开发实战>) <裸机开发实战>第13章 ...

  4. 二叉查找树实现实例(C语言)

    /* search_tree.h */ #ifndef _SEARCH_TREE_H #define _SEARCH_TREE_H struct tree_node; typedef struct t ...

  5. eclipse中java项目转成Web项目

    1.找到项目目录下的.project文件 2.编辑.project文件,找到<natures>...</natures> 3.2中找到的结点中加下面的的代码 <natur ...

  6. 在Ubuntu下设置默认编辑器

    在默认情况下,Ubuntu 系统会为用户预设程序.就拿文本编辑器来说吧,Ubuntu 预设的是 Nano,对某些朋友来说,使用 Vim 可能更得心应手些.那么如何更改这些预设的程序呢? 你可以使用 s ...

  7. 【VMware】宿主机连接wifi,虚拟机中的Linux系统配置连接wifi

    环境描述 宿主机:Windows 10 64bit 虚拟机:Centos 第一步:虚拟机设置 选择连接方式为NAT 第二步:设置宿主机的wifi 控制面板>>网络和Internet> ...

  8. 调用网易有道词典api

    # -*- coding: utf-8 -*- #python 27 #xiaodeng #调用网易有道词典api import urllib import json class Youdao(): ...

  9. 使用loadrunner对https协议(单双向SSL)的web端性能测试 (转)

    1.项目背景 1.1 单双向SSL的含义及部署 单向SSL即我们说到的https协议. 特点是,浏览器需要请求验证服务器证书: 基本含义是:一个安全通信通道,它基于HTTP开发,用于在客户计算机和服务 ...

  10. linux脚本加密shc

    linxu的shell脚本看下源码,都能明白含义.加密也是很关键的 01.安装shc加密 http://www.datsi.fi.upm.es/~frosal/sources/   ###下载源码 百 ...