一个令人困惑的低效SQL
整理之前的优化案例,觉得下面这个应该是开发很难发现也会很困惑的一个低效SQL。
看下面这个SQL。你看到这个SQL会不会感觉很正常。其实我刚看到也觉得正常得不得了。但是测试后它确实效率很低。
select test1.object_id, test1.object_name, test1.owner, test1.object_type
from test1
inner join (select test1.object_id as object_id1,
test2.object_id as object_id2,
test3.object_id as object_id3,
test1.object_name as object_name1
from test1, test2, test3
where test1.object_id = test2.object_id
and test1.object_id = test3.object_id
and test2.object_id = test3.object_id) temp
on test1.object_id = temp.object_id1;
测试数据:
create table test1 as select object_id,object_name,owner,object_type from dba_objects;--13433行
create table test2 as select * from test1;--13433行
create table test3 as select * from test1;--13433行
create index i_test1_id on test1(object_id);
analyze table test1 compute statistics for table for all indexes for all indexed columns;
analyze table test2 compute statistics for table;
analyze table test3 compute statistics for table;
原始SQL:
SQL> select test1.object_id, test1.object_name, test1.owner, test1.object_type
2 from test1
3 inner join (select test1.object_id as object_id1,
4 test2.object_id as object_id2,
5 test3.object_id as object_id3,
6 test1.object_name as object_name1
7 from test1, test2, test3
8 where test1.object_id = test2.object_id
9 and test1.object_id = test3.object_id
10 and test2.object_id = test3.object_id) temp
11 on test1.object_id = temp.object_id1;
已选择13433行。
已用时间: 00: 00: 00.41
执行计划
----------------------------------------------------------
Plan hash value: 3055531907
-------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
-------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 68 | 62 (4)| 00:00:01 |
| 1 | NESTED LOOPS | | | | | |
| 2 | NESTED LOOPS | | 1 | 68 | 62 (4)| 00:00:01 |
|* 3 | HASH JOIN | | 1 | 30 | 60 (4)| 00:00:01 |
| 4 | TABLE ACCESS FULL | TEST3 | 13433 | 170K| 24 (0)| 00:00:01 |
|* 5 | HASH JOIN | | 13433 | 223K| 35 (3)| 00:00:01 |
| 6 | INDEX FAST FULL SCAN | I_TEST1_ID | 13433 | 53732 | 10 (0)| 00:00:01 |
| 7 | TABLE ACCESS FULL | TEST2 | 13433 | 170K| 24 (0)| 00:00:01 |
|* 8 | INDEX RANGE SCAN | I_TEST1_ID | 1 | | 1 (0)| 00:00:01 |
| 9 | TABLE ACCESS BY INDEX ROWID| TEST1 | 1 | 38 | 2 (0)| 00:00:01 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
3 - access("TEST1"."OBJECT_ID"="TEST3"."OBJECT_ID" AND
"TEST2"."OBJECT_ID"="TEST3"."OBJECT_ID")
5 - access("TEST1"."OBJECT_ID"="TEST2"."OBJECT_ID")
8 - access("TEST1"."OBJECT_ID"="TEST1"."OBJECT_ID")
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
4086 consistent gets
0 physical reads
0 redo size
549707 bytes sent via SQL*Net to client
10260 bytes received via SQL*Net from client
897 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
13433 rows processed
是不是开始困惑了?子查询我三个表数据一模一样,都是用的inner join,按我们造的测试数据都是从dba_objects的,object_id应该也是唯一的。
test1、test2和test3关联结果应该跟三个表的结果集是一样的才对,怎么三个表hash join的结果竟然是1.最后再跟test1进行一次join,优化器很容易就选择了nested loop。
其实这个我困惑,猜想,优化器由test1.object_id = test2.object_id and test1.object_id = test3.object_id,可以推导出test2.object_id=test3.object_id,
但是限制条件中又写了一遍,它倒评估错了。至于啥原理,我也没搞懂。
去掉test2.object_id=test3.object_id限制,再看下执行计划:
SQL> select test1.object_id, test1.object_name, test1.owner, test1.object_type
2 from test1
3 inner join (select test1.object_id as object_id1,
4 test2.object_id as object_id2,
5 test3.object_id as object_id3,
6 test1.object_name as object_name1
7 from test1, test2, test3
8 where test1.object_id = test2.object_id
9 and test1.object_id = test3.object_id
10 /*and test2.object_id = test3.object_id*/) temp
11 on test1.object_id = temp.object_id1;
已选择13433行。
已用时间: 00: 00: 00.36
执行计划
----------------------------------------------------------
Plan hash value: 144638806
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 13433 | 892K| 84 (3)| 00:00:02 |
|* 1 | HASH JOIN | | 13433 | 892K| 84 (3)| 00:00:02 |
| 2 | TABLE ACCESS FULL | TEST3 | 13433 | 170K| 24 (0)| 00:00:01 |
|* 3 | HASH JOIN | | 13433 | 721K| 60 (4)| 00:00:01 |
| 4 | TABLE ACCESS FULL | TEST2 | 13433 | 170K| 24 (0)| 00:00:01 |
|* 5 | HASH JOIN | | 13433 | 550K| 35 (3)| 00:00:01 |
| 6 | INDEX FAST FULL SCAN| I_TEST1_ID | 13433 | 53732 | 10 (0)| 00:00:01 |
| 7 | TABLE ACCESS FULL | TEST1 | 13433 | 498K| 24 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("TEST1"."OBJECT_ID"="TEST3"."OBJECT_ID")
3 - access("TEST1"."OBJECT_ID"="TEST2"."OBJECT_ID")
5 - access("TEST1"."OBJECT_ID"="TEST1"."OBJECT_ID")
统计信息
----------------------------------------------------------
0 recursive calls
0 db block gets
1161 consistent gets
0 physical reads
0 redo size
549707 bytes sent via SQL*Net to client
10260 bytes received via SQL*Net from client
897 SQL*Net roundtrips to/from client
0 sorts (memory)
0 sorts (disk)
13433 rows processed
这次评估是没错的,外部的test1和三个表关联的结果是一样的,再走hash join也是理所当然的。
这个案例,不是条件写得越多越好。开发可能觉得,把能写的条件都写进去,可是优化器的评估却出乎意料。
至于优化器评估原理,还没搞懂:(
一个令人困惑的低效SQL的更多相关文章
- python logging模块可能会令人困惑的地方
python logging模块主要是python提供的通用日志系统,使用的方法其实挺简单的,这块就不多介绍.下面主要会讲到在使用python logging模块的时候,涉及到多个python文件的调 ...
- 如何找出MySQL数据库中的低效SQL语句
面对业务的迅猛发展,DBA的一项重要工作就是及时发现数据库中的低效SQL语句,有的可以立刻着手解决(比如缺少合适的索引),有的需要尽快反馈给开发人员进行修改. MySQL数据库有几个配置选项可以帮助我 ...
- Java安全编码:糟糕的在线建议和令人困惑的APIs
对于程序员和软件开发人员来说,网络论坛提供了一个交流知识和寻找具体编码难题答案的好地方.遗憾的是,他们并不总是准确信息的来源. 弗吉尼亚理工大学的一组研究人员分析了数百篇关于Stack Overflo ...
- 如果一条SQL语句太长,我们可以通过回车键来创建一个新行来编写SQL语句,SQL语句的命令结束符为分号(;)。
1.如果一条SQL语句太长,我们可以通过回车键来创建一个新行来编写SQL语句,SQL语句的命令结束符为分号(;). 2.select查询的多个字段之间要用逗号“,”分割,如果查询涉及多个表,那多个表之 ...
- PLSQL_低效SQL的识别和查询汇总(案例)
2014-12-18 Created By BaoXinjian
- Julie D. Saba:儿童肿瘤学是一个令人惊奇的领域
编者按 作为一名儿童肿瘤学家,工作中充满了挑战与机遇.近几十年来,世界各地的儿童肿瘤的发病率呈持续上升的趋势.许多人认为这不仅是由于诊断水平的提高,而且是因为儿童肿瘤的潜在风险也确实在增加.据英国儿童 ...
- 一个看似很简单的SQL却难倒了很多人
一个选课表,有学生id,课程id,老师id,要求选出同时选了语文和数学的学生 USE [tempschool] GO /****** 对象: Table [dbo].[SelectC] 脚本日期: 0 ...
- 通过一个小问题来学习SQL关联查询
原话题: 是关于一个left join的,没有技术难度,但不想清楚不一定能回答出正确答案来: TabA表有三个字段Id,Col1,Col2 且里面有一条数据1,1,2 TabB表有两个字段Id,Col ...
- 一个简单的ORM制作(SQL帮助类)
一个简单的ORM制作大概需要以下几个类: SQL执行类 CURD操作类 其他酱油类 先从SQL执行类说起,可能会涉及数据库的迁移等问题,所以需要定义一个接口以方便迁移到其他数据库, 事务没提供命名,若 ...
随机推荐
- 全新的手势,侧滑返回、全局右滑返回都OUT啦!
前言 Android快速开发框架-ZBLibrary 最近将以前的 全局右滑返回 手势功能改成了 底部左右滑动手势. 为什么呢?为了解决滑动返回手势的问题. 目前有3种滑动返回手势 一.侧滑返回 代表 ...
- 将CGPoint、CGSize、CGRect等放进数组的方法
在oc中,数组中只能存放NSObject类型的数据,所以如果将CGPoint.CGSize.CGRect这些数据存到数组中,我们需要将他们转换为对象类型才可以, 可以借助NSValue,它是用来将基本 ...
- c++算法联系,冒泡排序,bubble sort,插入排序,insert sort,
#include <iostream.h> #define MAX 100 void dispaly(int a[],int n) { for(int i=0;i<n;i+ ...
- 在eclipse中运行storm-starter
开源软件官网提供的demo无疑是学习开源软件的最好的最原始的样例. 在Storm官网里下载apache-storm-0.9.6.zip,里面\examples\storm-starter\src\jv ...
- DROP--删除表
DROP TABLE table_name; 说明: 1.必须有表的权限 2.表不能有外键约束
- 【itclx面向对象二】窥探itcl面向编程源码
从上一篇博客看出,itcl的语法其实不难,但是有个缺点,编程习惯与当前类似C++常见的面向编程还是有些区别,并且在大型项目实施中这种方式很费劲. 于是有了itclx. 例如: 1.成员变量.成员方法调 ...
- 巧用selectKey
在Ibatis中,insert()的返回值为一个Object的主键,其实这个Object的主键是这样的来的:如果在bean的xml文件中设置了插入的keyProperty,则insert()方法返回的 ...
- dedecms安装步骤
GD支持:PHP设置-PHP扩展-php_gd2 初始化数据体验包:点击下载:或者点击取消 如果是本地安装在数据库的在数据库用户名选择默认的(root),密码为空 主要是如果是基于远程服务器的 ...
- RTTI和反射
JAVA 运行时识别对象和类的信息,主要有两种方式:一种是传统的RTTI,它假定我们在编译时已经知道了所有的类型:另一种是"反射"机制,它允许我们在运行时发现和使用类的信息. 参考 ...
- 20160512--hibernate--缓存
缓存 缓存的作用主要用来提高性能,可以简单的理解成一个Map:使用缓存涉及到三个操作:把数据放入缓存.从缓存中获取数据.删除缓存中的无效数据. 原理模拟分析:(不能运行,只是模拟)(缓存实现复杂, ...