楼主: fusnow

关于hint

[复制链接]
论坛徽章:
15
2010新春纪念徽章
日期:2010-03-01 11:08:292013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-09 18:14:48ITPUB 11周年纪念徽章
日期:2012-09-28 17:34:42灰彻蛋
日期:2012-01-11 12:03:01ITPUB十周年纪念徽章
日期:2011-11-01 16:25:51ITPUB十周年纪念徽章
日期:2011-09-27 16:33:28ITPUB官方微博粉丝徽章
日期:2011-06-28 19:45:362010广州亚运会纪念徽章:马术
日期:2011-05-15 07:47:292010广州亚运会纪念徽章:龙舟
日期:2010-11-17 15:11:54
11#
发表于 2010-1-31 12:50 | 只看该作者
楼主好样的。。
支持~

使用道具 举报

回复
论坛徽章:
11
生肖徽章2007版:鸡
日期:2009-05-11 17:35:332011新春纪念徽章
日期:2011-01-04 10:37:102010年世界杯参赛球队:西班牙
日期:2010-06-24 17:14:482010年世界杯参赛球队:尼日利亚
日期:2010-03-29 21:25:292010新春纪念徽章
日期:2010-03-01 11:19:072010年世界杯参赛球队:阿根廷
日期:2010-01-27 14:53:342010年世界杯参赛球队:斯洛伐克
日期:2010-01-22 15:04:21生肖徽章2007版:兔
日期:2009-11-11 17:42:152009日食纪念
日期:2009-07-22 09:30:00生肖徽章2007版:鸡
日期:2009-07-22 08:42:09
12#
发表于 2010-1-31 13:32 | 只看该作者
呵呵,好贴啊!

使用道具 举报

回复
论坛徽章:
86
2015中国数据库技术大会纪念徽章
日期:2015-04-24 16:04:24马上有车
日期:2014-02-19 11:55:14马上有车
日期:2014-02-18 16:41:112014年新春福章
日期:2014-02-18 16:41:11优秀写手
日期:2013-12-18 09:29:11日产
日期:2013-10-17 08:44:39马自达
日期:2013-08-26 16:28:022013年新春福章
日期:2013-02-25 14:51:24ITPUB 11周年纪念徽章
日期:2012-10-23 16:55:51马上有房
日期:2014-02-19 11:55:14
13#
发表于 2010-1-31 13:59 | 只看该作者
好好,希望楼主能继续更新下去。

使用道具 举报

回复
招聘 : 项目经理
论坛徽章:
25
会员2007贡献徽章
日期:2007-09-26 18:42:10马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:142012新春纪念徽章
日期:2012-02-13 15:11:522012新春纪念徽章
日期:2012-02-13 15:11:522012新春纪念徽章
日期:2012-02-13 15:11:522012新春纪念徽章
日期:2012-02-13 15:11:522012新春纪念徽章
日期:2012-02-13 15:11:52
14#
 楼主| 发表于 2010-1-31 20:02 | 只看该作者
query block的名字

这段的实际应用意义不大,研究这个主要为了好玩。

前面提到我们可以用qb_name这个hint来定义SQL里面的query block的名字,实际上如果我们多看看
dbms_xplan.display_cursor的advanced模式的输出,或者看看10053 trace,或者看看10132 trace,就能发现其实oracle的CBO会
自己定义query block的名字,而且你会发现对于固定的SQL,这些query block的名字都是固定的。

这里打算介绍一下oracle是如何确定这些query block名字的,这已经超出了hint的范畴,但是如果你看到oracle给你列出这样的一条Hint,
了解query block的命名的由来对你读懂这个hint是有帮助的:
LEADING(@"SEL$F5BB74E1" "D"@"SEL$1" "EMPLOYEES"@"SEL$2")
--如果SQL多次出现employees这张表,你知道这个Hint是指哪个employees么?

其实一般来说,oracle对query block的命名还是很直观的,
如果是一个select,那么就命名为SEL$n,这里的n是1,2,3...
如果是delete,就命名为DEL$n,是update,就命名为UPD$n,是insert,就叫INS$n, 是merge,就叫MISC$n...

下面我举出两个例子,一个select, 一个delete,其他的大家可以自己去试。

explain plan for
select * from employees where salary = (select max(salary) from employees);

select * from table(dbms_xplan.display(null,null,'alias'));

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$1 / EMPLOYEES@SEL$1
   2 - SEL$2
   3 - SEL$2 / EMPLOYEES@SEL$2
   
explain plan for
delete from employees;

select * from table(dbms_xplan.display(null,null,'alias'));
Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - DEL$1
   2 - DEL$1 / EMPLOYEES@DEL$1



那为什么会出现类似于SEL$F5BB74E1这样的query block呢?这是因为oracle做了sql转化,我们知道oracle处于性能考虑会
把一些子查询转化为表与表连接的模式,SQL转化是个比较有意思的话题,包含的东西也很多(不仅仅针对子查询),
对于子查询而言,就是当你的SQL包含了子查询或者视图,oracle会对你的SQL进行转化,当然这种转化在10g里是基于cost的,也就是说如果转化后发现cost太高,这个转化就不会被采用。

对于oracle的SQL转化主要是做:
1) 转化SPJ (select + project + join) view
2) unnest Subquery
3) merge Complex View (mergable complex view的定义可见于oracle performance tuning guide)
4) Predicate Pushing, or expense, 物化视图重写,等。。。


那么当转化发生后,SQL的结构就变了,所以oracle会为转化后的SQL生成新的query block,为了和没有发生转化的SQL有所区别,
oracle在这里采用比较特殊的格式。

转化后oracle会把原来的一个外围的query block(假如名字是a), 和一个内部的query block(假如名字是b), 转化生成的新的query block的名字应该是a 和 b 的hash函数。

看看下面的例子:
SQL1,这是一个多层嵌套,每个嵌套的子查询都可以被转化,我们能看到oracle在做完转化后生成的新的query block为SEL$9C113579。
explain plan for
select /*+ qb_name(qb1) */ * from
  (select /*+ qb_name(qb2) */ * from
    (select /*+ qb_name(qb3) */ * from employees));

select * from table(dbms_xplan.display(null,null,'alias'));

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$9C113579 / EMPLOYEES@QB3

SQL2,是SQL1的第二层嵌套的查询,可以看到转化后生成的query block名字是SEL$1B0F72FE
explain plan for
select /*+ qb_name(qb2) */ * from
    (select /*+ qb_name(qb3) */ * from employees);

select * from table(dbms_xplan.display(null,null,'alias'));

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$1B0F72FE / EMPLOYEES@QB3

SQL3,最外面得查询还是叫qb1,在里面随便套了一个子查询,但是我们把第二层查询的名字定义为SEL$1B0F72FE。
explain plan for
select /*+ qb_name(qb1) */ * from
  (select /*+ qb_name(SEL$1B0F72FE) */ * from departments);

select * from table(dbms_xplan.display(null,null,'alias'));

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------
   1 - SEL$9C113579 / EMPLOYEES@SEL$1B0F72FE

可以看到转化后的query block的名字是SEL$9C113579,和我们的第一个SQL转化后得到的query block名字一致,这就可以证明,当oracle对SQL的子查询进行了转化后,
会生成新的query block的名字,这个名字是由原来的外围query block名字和原来的内层的query block名字决定的,估计是一个hash 函数。

如果我们把外面的query block名字改掉,得到的新的转化后的query block名字就会变了:

explain plan for
select /*+ qb_name(qbnew) */ * from
  (select /*+ qb_name(SEL$1B0F72FE) */ * from departments);

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$90C23CFB / DEPARTMENTS@SEL$1B0F72FE  --变了


下面这个例子可以看出,我把第二层的子查询做了一些改动,又套上了一些其他的子查询,但是使用no_merge hint来避免里面的子查询被oracle的CBO转化掉:
explain plan for
select /*+ qb_name(qb1) */ * from
  (select /*+ qb_name(SEL$1B0F72FE) */ * from employees where department_id in
                                          (select /*+ no_merge */ max(department_id) from departments where location_id in
                                          (select /*+ no_merge */ max(location_id) from locations)));

select * from table(dbms_xplan.display(null,null,'alias'));

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$9C113579 / EMPLOYEES@SEL$1B0F72FE
   2 - SEL$9C113579 / EMPLOYEES@SEL$1B0F72FE
   3 - SEL$1
   4 - SEL$1        / DEPARTMENTS@SEL$1
   5 - SEL$1        / DEPARTMENTS@SEL$1
   6 - SEL$2
   7 - SEL$2        / LOCATIONS@SEL$2

可以看到,即使把内层的SQL做了这么多改变,最终转化出来的query block的名字还叫SEL$9C113579。



最后看一个特殊的例子:

这里是一个简单的2个表连接,没有子查询,但是我们可以看到在query block的名字是SEL$58A6D7F6,和我前面说的规则并不一样,为什么会这样?

explain plan for
select department_id from departments d join locations l on d.location_id=l.location_id where l.city='Beijing';

--------------------------------------------------------------------------------------------------
| Id  | Operation                     | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT              |                  |     4 |    76 |     3   (0)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID  | DEPARTMENTS      |     4 |    28 |     1   (0)| 00:00:01 |
|   2 |   NESTED LOOPS                |                  |     4 |    76 |     3   (0)| 00:00:01 |
|   3 |    TABLE ACCESS BY INDEX ROWID| LOCATIONS        |     1 |    12 |     2   (0)| 00:00:01 |
|*  4 |     INDEX RANGE SCAN          | LOC_CITY_IX      |     1 |       |     1   (0)| 00:00:01 |
|*  5 |    INDEX RANGE SCAN           | DEPT_LOCATION_IX |     4 |       |     0   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$58A6D7F6 / D@SEL$1
   3 - SEL$58A6D7F6 / L@SEL$1
   4 - SEL$58A6D7F6 / L@SEL$1
   5 - SEL$58A6D7F6 / D@SEL$1


原因在于这个SQL使用了ANSI的表连接语法,oracle在解析“join locations l on”这样的条件的时候会把它看做是一个子查询,于是乎会做转化,于是乎生成SEL$58A6D7F6这样的query block名字。

在10053 trace里面会看到(VIEW MERGE SEL$2; SEL$1)这样的字样, 然后query被转化为:
select department_id from departments d join locations l on d.location_id=l.location_id where l.city='Beijing'

10053 trace的部分内容:
SQL:******* UNPARSED QUERY IS *******
SELECT "D"."DEPARTMENT_ID" "DEPARTMENT_ID" FROM "HR"."DEPARTMENTS" "D","HR"."LOCATIONS" "L" WHERE "L"."CITY"='Beijing' AND "D"."LOCATION_ID"="L"."LOCATION_ID"
Query block (04B6A5E0) unchanged
Registered qb: SEL$58A6D7F6 0x4b6a5e0 (VIEW MERGE SEL$2; SEL$1)
  signature (): qb_name=SEL$58A6D7F6 nbfros=2 flg=0
    fro(0): flg=0 objn=51905 hint_alias="D"@"SEL$1"
    fro(1): flg=0 objn=51900 hint_alias="L"@"SEL$1"
.........................................................
.........................................................
****************
QUERY BLOCK TEXT
****************
select department_id from departments d join locations l on d.location_id=l.location_id where l.city='Beijing'



这对我们有什么影响吗? 其实影响不大,我前面也说这一段不是为了使用,仅仅为了好玩,不过假如上面的SQL作为我们下面语句的子查询,并且我想让表locations走全表扫描,我按理应该可以这样写hint:   
explain plan for
select /*+ full(@inner l) */ * from employees where department_id in
(select /*+ qb_name(inner) */ department_id from departments d join locations l on d.location_id=l.location_id where l.city='Beijing');

-------------------------------------------------------------------------------------------------------
| Id  | Operation                         | Name              | Rows  | Bytes | Cost (%CPU)| Time     |
-------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                  |                   |    39 |  3159 |     6  (17)| 00:00:01 |
|   1 |  TABLE ACCESS BY INDEX ROWID      | EMPLOYEES         |    10 |   680 |     1   (0)| 00:00:01 |
|   2 |   NESTED LOOPS                    |                   |    39 |  3159 |     6  (17)| 00:00:01 |
|   3 |    VIEW                           | VW_NSO_1          |     4 |    52 |     3   (0)| 00:00:01 |
|   4 |     HASH UNIQUE                   |                   |     4 |    76 |            |          |
|   5 |      TABLE ACCESS BY INDEX ROWID  | DEPARTMENTS       |     4 |    28 |     1   (0)| 00:00:01 |
|   6 |       NESTED LOOPS                |                   |     4 |    76 |     3   (0)| 00:00:01 |
|   7 |        TABLE ACCESS BY INDEX ROWID| LOCATIONS         |     1 |    12 |     2   (0)| 00:00:01 |
|*  8 |         INDEX RANGE SCAN          | LOC_CITY_IX       |     1 |       |     1   (0)| 00:00:01 |
|*  9 |        INDEX RANGE SCAN           | DEPT_LOCATION_IX  |     4 |       |     0   (0)| 00:00:01 |
|* 10 |    INDEX RANGE SCAN               | EMP_DEPARTMENT_IX |    10 |       |     0   (0)| 00:00:01 |
-------------------------------------------------------------------------------------------------------

Query Block Name / Object Alias (identified by operation id):
-------------------------------------------------------------

   1 - SEL$78C29F24 / EMPLOYEES@SEL$2
   3 - SEL$49A70C8F / VW_NSO_1@SEL$78C29F24
   4 - SEL$49A70C8F
   5 - SEL$49A70C8F / D@SEL$1
   7 - SEL$49A70C8F / L@SEL$1
   8 - SEL$49A70C8F / L@SEL$1
   9 - SEL$49A70C8F / D@SEL$1
  10 - SEL$78C29F24 / EMPLOYEES@SEL$2
  
但如你所见,hint似乎没有生效,这个原因就是尽管我们给
(select /*+ qb_name(inner) */ department_id from departments d join locations l on d.location_id=l.location_id where l.city='Beijing')
这个子查询定义了query block的名字,但是因为这个是ANSI语法的连接,oracle会做一次转换,这次转化会给子查询生成一个新的query block(SEL$49A70C8F),
这进一步导致/*+ full(@inner l) */这个针对inner的Hint失效。

要想达到目的的方法也很简单, 比如hint可以这么写:
select * from employees where department_id in
(select /*+ full(l) */ department_id from departments d join locations l on d.location_id=l.location_id where l.city='Beijing');

或者既然已经知道了新的query block的名字,我们就可以这么写(当然这种写法很奇怪,一般也不应该怎么写就是了):
select /*+ full(@SEL$49A70C8F l) */ * from employees where department_id in
(select /*+ qb_name(inner) */ department_id from departments d join locations l on d.location_id=l.location_id where l.city='Beijing');

--如前所述,这里/*+ qb_name(inner) */还是需要的,否则转化后的query block名字又会不一样。


如果说能从这里例子总结点什么出来的话,就是如果你在oracle里写SQL,并估计以后可能需要给它加Hint,为了使用Hint方便,可以考虑尽量使用oracle的语法.


[ 本帖最后由 fusnow 于 2010-2-5 09:55 编辑 ]

使用道具 举报

回复
论坛徽章:
5
生肖徽章2007版:鸡
日期:2008-01-02 17:35:53奥运会纪念徽章:羽毛球
日期:2008-06-23 12:00:05奥运会纪念徽章:柔道
日期:2008-07-04 09:42:36奥运会纪念徽章:皮划艇激流回旋
日期:2008-08-12 14:50:402010新春纪念徽章
日期:2010-03-01 11:19:07
15#
发表于 2010-2-1 10:21 | 只看该作者
好帖,对于使用 hint 和 对CBO的信任 间关系说的很好。

我感觉,hint就是CBO的补丁,DBA用这个工具来实现CBO在具体环境中的最佳实践。
哪怕到了11g,CBO仍然不可能尽善尽美,有时聪明有时傻,hint就是在CBO犯浑的时候给它扭转回来。
所以我觉得,hint不可滥用,不可不用。

使用道具 举报

回复
论坛徽章:
11
CTO参与奖
日期:2009-02-03 14:04:30鲜花蛋
日期:2012-04-28 09:12:592010广州亚运会纪念徽章:卡巴迪
日期:2011-01-28 16:58:54ITPUB9周年纪念徽章
日期:2010-10-08 09:34:022010年世界杯参赛球队:斯洛文尼亚
日期:2010-02-03 05:31:212010年世界杯参赛球队:斯洛伐克
日期:2010-01-01 15:02:272010年世界杯参赛球队:塞尔维亚
日期:2009-12-31 09:53:35祖国60周年纪念徽章
日期:2009-10-09 08:28:00数据库板块每日发贴之星
日期:2009-08-17 01:01:022009日食纪念
日期:2009-07-22 09:30:00
16#
发表于 2010-2-1 10:26 | 只看该作者
比贴得顶。

使用道具 举报

回复
论坛徽章:
5
2010新春纪念徽章
日期:2010-01-04 08:33:082010新春纪念徽章
日期:2010-03-01 11:08:332010广州亚运会纪念徽章:击剑
日期:2010-11-22 15:29:20ITPUB十周年纪念徽章
日期:2011-11-01 16:24:042012新春纪念徽章
日期:2012-01-04 11:54:26
17#
发表于 2010-2-1 10:28 | 只看该作者
受教了,这个太强了

使用道具 举报

回复
论坛徽章:
28
八级虎吧徽章
日期:2009-03-03 17:57:50蜘蛛蛋
日期:2013-03-04 10:05:46迷宫蛋
日期:2013-05-21 11:58:33蛋疼蛋
日期:2013-07-18 16:11:37蜘蛛蛋
日期:2013-07-23 17:23:40ITPUB社区12周年站庆徽章
日期:2013-10-08 14:56:08本田
日期:2013-11-20 12:38:09奥迪
日期:2013-12-20 10:29:33一汽
日期:2014-01-10 16:21:57夏利
日期:2014-01-21 10:39:35
18#
发表于 2010-2-1 13:35 | 只看该作者
学习LZ的专研精神

使用道具 举报

回复
招聘 : 项目经理
论坛徽章:
25
会员2007贡献徽章
日期:2007-09-26 18:42:10马上有对象
日期:2014-02-19 11:55:14马上有钱
日期:2014-02-19 11:55:14马上有房
日期:2014-02-19 11:55:14马上有车
日期:2014-02-19 11:55:142012新春纪念徽章
日期:2012-02-13 15:11:522012新春纪念徽章
日期:2012-02-13 15:11:522012新春纪念徽章
日期:2012-02-13 15:11:522012新春纪念徽章
日期:2012-02-13 15:11:522012新春纪念徽章
日期:2012-02-13 15:11:52
19#
 楼主| 发表于 2010-2-1 16:20 | 只看该作者
谢谢rollingpig兄加精

使用道具 举报

回复
论坛徽章:
821
授权会员
日期:2007-08-10 01:06:30山治
日期:2019-11-15 22:34:592015年新春福章
日期:2015-03-06 11:57:31暖羊羊
日期:2015-03-04 14:50:37马上有钱
日期:2014-12-21 16:14:33马上加薪
日期:2014-11-23 19:24:42 2014年世界杯参赛球队: 德国
日期:2014-07-09 15:28:06ITPUB元老
日期:2008-08-24 00:06:57会员2007贡献徽章
日期:2007-09-26 18:42:10托尼托尼·乔巴
日期:2020-03-23 10:49:16
20#
发表于 2010-2-1 16:45 | 只看该作者
好久没有新的精华贴了,学习

使用道具 举报

回复

您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

TOP技术积分榜 社区积分榜 徽章 团队 统计 知识索引树 积分竞拍 文本模式 帮助
  ITPUB首页 | ITPUB论坛 | 数据库技术 | 企业信息化 | 开发技术 | 微软技术 | 软件工程与项目管理 | IBM技术园地 | 行业纵向讨论 | IT招聘 | IT文档
  ChinaUnix | ChinaUnix博客 | ChinaUnix论坛
CopyRight 1999-2011 itpub.net All Right Reserved. 北京盛拓优讯信息技术有限公司版权所有 联系我们 未成年人举报专区 
京ICP备16024965号-8  北京市公安局海淀分局网监中心备案编号:11010802021510 广播电视节目制作经营许可证:编号(京)字第1149号
  
快速回复 返回顶部 返回列表