Oracle11g oracle 11g执行计划行为
我有一个非常简单的查询,它通过rowid执行过滤和连接Oracle11g oracle 11g执行计划行为,oracle11g,sql-execution-plan,Oracle11g,Sql Execution Plan,我有一个非常简单的查询,它通过rowid执行过滤和连接 SELECT * FROM BOOKING.BOOKING_GRID BG, BOOKING.BOOKING_STATES BS WHERE BG.hotel=128 AND BS.ROWID =BG.BOOKINGSTATE; 当我解释计划时,我得到: PLAN_TABLE_OUTPUT
SELECT *
FROM BOOKING.BOOKING_GRID BG,
BOOKING.BOOKING_STATES BS
WHERE BG.hotel=128
AND BS.ROWID =BG.BOOKINGSTATE;
当我解释计划时,我得到:
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 1597031677
--------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes |TempSpc| Cost (%CPU)| Time |
--------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 6137K| 1041M| | 1763K (1)| 05:48:27 |
|* 1 | HASH JOIN | | 6137K| 1041M| 538M| 1763K (1)| 05:48:27 |
|* 2 | INDEX UNIQUE SCAN| BOOKING_GRIDPK | 6137K| 468M| | 547K (1)| 01:48:05 |
|* 3 | INDEX RANGE SCAN| BOOKING_GRID_INDEX5 | 6137K| | | 90388 (1)| 00:17:52 |
| 4 | TABLE ACCESS FULL| BOOKING_STATES | 158M| 14G| | 365K (2)| 01:12:14 |
--------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
1 - access("BS".ROWID="BG"."BOOKINGSTATE")
2 - access("BG"."HOTEL"=128)
3 - access("BG"."HOTEL"=128)
预订网格的索引为:
BOOKING BOOKING_GRIDPK UNIQUE VALID IOT - TOP N NO NO HOTEL, DAY, BOOKINGSTATE
BOOKING BOOKING_GRID_UNIQ UNIQUE VALID NORMAL N NO NO HOTEL, DAY, BOOKING, VALIDITYSTART
BOOKING BOOKING_GRID_INDEX5 NONUNIQUE VALID NORMAL N NO NO HOTEL, BOOKINGSTATUS, ISDAYUSE, DAY
BOOKING BOOKING_GRID_INDEX7 NONUNIQUE VALID NORMAL N NO NO HOTEL, BOOKING, VALIDITYSTART
BOOKING BOOKING_GRID_INDEX10 NONUNIQUE VALID NORMAL N NO NO HOTEL, ISDAYUSE, BOOKINGSTATUS, DAY
美国各州的指数如下:
BOOKING BOOKING_STATES_PK UNIQUE VALID NORMAL N NO NO HOTEL, BOOKING, VALIDITYSTART
BOOKING BOOKING_STATES_INDEX2 NONUNIQUE VALID NORMAL N NO NO HOTEL, YIELDROOMTYPE, BOOKEDROOMTYPE, ROOMTYPE
BOOKING BOOKING_STATES_BOOKING NONUNIQUE VALID NORMAL N NO NO HOTEL, BOOKING, BOOKINGSTATUS
BOOKING BOOKING_NOSEGMENT_INDEX NONUNIQUE VALID FUNCTION-BASED NORMAL N NO ENABLED NO SYS_NC00034$ TO_NUMBER(DECODE(TO_CHAR("MARKETSEGMENT"),NULL,DECODE("BOOK",0,NULL,TO_CHAR(DECODE("ISDAYUSE",'N',DECODE("ISSHARED",'N',DECODE("BOOKINGSTATUS",'B',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"),'I',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"),'W',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"))))))))
BOOKING BOOKING_NORATE_CODE_INDEX NONUNIQUE VALID FUNCTION-BASED NORMAL N NO ENABLED NO SYS_NC00033$ TO_NUMBER(DECODE(TO_CHAR("RATECODE"),NULL,DECODE("BOOK",0,NULL,TO_CHAR(DECODE("ISDAYUSE",'N',DECODE("ISSHARED",'N',DECODE("BOOKINGSTATUS",'B',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"),'I',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"),'W',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"))))))))
BOOKING BOOKING_NOBOOKINGTYPE_INDEX NONUNIQUE VALID FUNCTION-BASED NORMAL N NO ENABLED NO SYS_NC00032$ TO_NUMBER(DECODE(TO_CHAR("BOOKINGTYPE"),NULL,DECODE("BOOK",0,NULL,TO_CHAR(DECODE("ISDAYUSE",'N',DECODE("ISSHARED",'N',DECODE("BOOKINGSTATUS",'B',"HOTEL"*10000+LEAST("DEPARTURE","VALIDITYEND"))))))))
BOOKING BOOKING_STATES_BOOKING_TYPE NONUNIQUE VALID NORMAL N NO NO HOTEL, BOOKINGTYPE, ISDAYUSE, BOOKINGSTATUS
BOOKING BOOKING_STATES_CANCEL_INDEX NONUNIQUE VALID FUNCTION-BASED NORMAL N NO ENABLED NO SYS_NC00035$, SYS_NC00036$ DECODE("BOOKINGSTATUS",'c',"HOTEL",'C',"HOTEL")
BOOKING BOOKING_STATES_CANCEL_INDEX NONUNIQUE VALID FUNCTION-BASED NORMAL N NO ENABLED NO SYS_NC00035$, SYS_NC00036$ DECODE("BOOKINGSTATUS",'c',"CANCELREASON",'C',"CANCELREASON")
我不明白两件事:
PLAN_TABLE_OUTPUT
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Plan hash value: 4251203092
---------------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 591K| 100M| 643K (1)| 02:07:12 |
| 1 | NESTED LOOPS | | 591K| 100M| 643K (1)| 02:07:12 |
|* 2 | INDEX UNIQUE SCAN | BOOKING_GRIDPK | 591K| 45M| 52686 (1)| 00:10:25 |
|* 3 | INDEX RANGE SCAN | BOOKING_GRID_INDEX5 | 591K| | 8707 (1)| 00:01:44 |
| 4 | TABLE ACCESS BY USER ROWID| BOOKING_STATES | 1 | 98 | 1 (0)| 00:00:01 |
---------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access("BG"."HOTEL"=201)
3 - access("BG"."HOTEL"=201)
知道那里发生了什么吗
谢谢,
Renaud执行路径不同的原因是Oracle认为hotel=128时大约有600万行,而hotel=201时只有591000行。在较大的中间集的情况下,Oracle选择哈希连接而不是嵌套循环 我不明白的是:
AND BS.ROWID =BG.BOOKINGSTATE;
您正在将Oracle格式的行ID存储在一个名为BOOKINGSTATE的列中
好的,鉴于您确认BOOKINGSTATE确实包含Oracle ROWIDs,下面是为什么我要说您得到的是散列连接而不是嵌套循环连接: 首先,当Oracle读取一行时,它不只是一次读取一行,而是一次读取块。因此,要通过用户ROWID查找进行表访问的嵌套循环联接,它将在BOOKING_网格中找到一行,然后使用该ROWID读取该行处于BOOKING_状态的块。问题是,如果稍后在一个块中有另一行的rowid之前已经读取过,那么它将重新读取该块(当然,它可能被缓存)以获取另一行。有点像“打开块,获取一行,关闭框…然后再打开同一个框,获取另一行,关闭框,移动到下一个框” 另一方面,您的哈希联接是: -对较小集合中的行进行排序(在本例中是BOOKING_GRID中的行,其中hotel=128),将它们放入内存中 -使用多块读取的全表扫描状态-这里是提示。它一次读取多个块,并处理块中的所有行,而无需稍后重新读取。就像“打开框,处理框中的所有行,然后关闭框。” (有关上述内容的更多详细信息,请查看以下部分:
- 11.5.1全表扫描
- 11.5.3.1评估块而非行的I/O
- 11.6.3嵌套回路连接
- 11.6.4散列联接 )
顺便说一句,有点奇怪的是,它使用两个索引执行了两次“访问”(“BG”。“HOTEL”=128)”步骤-如何定义BOOKING\u GRIDPK和BOOKING\u GRID\u INDEX5索引?您要求两个表中的所有列,但该计划从未涉及预订网格表。)不同执行路径的原因是Oracle认为hotel=128时大约有600万行,而hotel=201时只有591000行。在较大的中间集的情况下,Oracle选择哈希连接而不是嵌套循环 我不明白的是:
AND BS.ROWID =BG.BOOKINGSTATE;
您正在将Oracle格式的行ID存储在一个名为BOOKINGSTATE的列中
好的,鉴于您确认BOOKINGSTATE确实包含Oracle ROWIDs,下面是为什么我要说您得到的是散列连接而不是嵌套循环连接: 首先,当Oracle读取一行时,它不只是一次读取一行,而是一次读取块。因此,要通过用户ROWID查找进行表访问的嵌套循环联接,它将在BOOKING_网格中找到一行,然后使用该ROWID读取该行处于BOOKING_状态的块。问题是,如果稍后在一个块中有另一行的rowid之前已经读取过,那么它将重新读取该块(当然,它可能被缓存)以获取另一行。有点像“打开块,获取一行,关闭框…然后再打开同一个框,获取另一行,关闭框,移动到下一个框” 另一方面,您的哈希联接是: -对较小集合中的行进行排序(在本例中是BOOKING_GRID中的行,其中hotel=128),将它们放入内存中 -使用多块读取的全表扫描状态-这里是提示。它一次读取多个块,并处理块中的所有行,而无需稍后重新读取。就像“打开框,处理框中的所有行,然后关闭框。” (有关上述内容的更多详细信息,请查看以下部分:
- 11.5.1全表扫描
- 11.5.3.1评估块而非行的I/O
- 11.6.3嵌套回路连接
- 11.6.4散列联接 )
顺便说一句,有点奇怪的是,它使用两个索引执行了两次“访问”(“BG”。“HOTEL”=128)”步骤-如何定义BOOKING\u GRIDPK和BOOKING\u GRID\u INDEX5索引?您要求两个表中的所有列,但该计划从未触及预订网格表。)当然,欢迎就如何自己调查提供任何建议:)。我试图深入研究V$SQL_计划,但我真的不知道如何从中获取信息。BG.BOOKINGSTATE代表什么?为什么要将它与ROWID进行比较?是的,Oracle提供了一种存储物理ROWID(应该是最快的加入方式)的方法,条件是它们永远不能更改(这是数据库中的一种设置)。这就是我们所做的,BG.bookingstate是BS中一行的rowid。如何保证rowid永远不会更改?您是否永远都不想将表移动到新表空间、收缩表以恢复空间、移动到新服务器、将此数据复制到开发/测试/qa服务器、分区表、使用表闪回等?告诉你吧,20年来我从未见过有人将rowid作为一个永久的键值来实现。更糟糕的是,在您的例子中,您看到的哈希联接中,通过索引避免rowid查找的任何性能好处都被否定了——事实上,rowid的性能甚至可能更差,因为它比数字键大。当然,对于如何由我自己进行调查的任何建议都是受欢迎的:)。我试着