常用的几种Hints优化一条SQL

来源:互联网 发布:net和java有什么区别 编辑:程序博客网 时间:2024/05/29 17:50
环境: oracle 10g R2 Select Count(*) From t_Ho_Order_Statistics --2032946Select Count(*) From t_Ho_Order_Info       --2032946其他都是小的维度表统计信息已经检查过了,差不多10天前的(不过我10天前跑过这个SQL,出来的执行计划一样),这里,这里就把注意力集中在两个大表连接的问题上,

后面的谓词过滤,这个SQL是程序里面拼出来的,每次谓词过滤条件可能不同。

文章主要是指出hints的基本用法,还有使用hints的误区
--<1>下面这条SQL要运行5分钟左右,出来的结果为0 原SQL和执行计划SQL> explain plan forSelect Count(*)  From (Select o.Order_Id,               o.Order_Version,               o.Guest_Name,               o.Order_Status_Id,               o.Order_Flow_Id,               o.Order_Occupt_Type_Id,               o.Pymt_Means_Id,               o.Contact_Name,               o.Reserv_Time,               h.Hotel_Name,               h.Address,               Arrival_Time,               Checkout_Time,               o.Use_Rule,               o.Guaranty_Type_Id,               Gt.Guaranty_Type_Desc,               o.Hbeorder_Id,               o.Order_Occupt_Type_Id Producttype,               o.Order_Type_Rule,               o.Hotel_Belong          From t_Ho_Order_Info o          Left Join t_Sd_Guaranty_Type Gt            On o.Guaranty_Type_Id = Gt.Guaranty_Type_Id         Inner Join t_Hp_Hotel h            On o.Hotel_Id = h.Hotel_Id         Inner Join t_Ho_Order_Statistics Os            On Os.Order_Id = o.Order_Id         Inner Join t_s_Employee e            On e.Employee_Id = o.Employee_Id         Where 1 = 1           And o.Guest_Name Like '%王明%'           And o.Reserv_Time >=               To_Date('2012-1-10 0:00:00', 'yyyy-mm-ddhh24-mi-ss')           And o.Reserv_Time <=               To_Date('2012-5-24 19:00:00', 'yyyy-mm-ddhh24-mi-ss')) T0Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT-------------------------------------------------------------------------------------------------------- Plan hash value: 1815111547---------------------------------------------------------------------------------------------------------| Id  | Operation                      | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |---------------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT               |                        |     1 |    49 | 11175  (38)| 00:02:15 ||   1 |  SORT AGGREGATE                |                        |     1 |    49 |            |          ||   2 |   NESTED LOOPS OUTER           |                        |  3430 |   164K| 11175  (38)| 00:02:15 ||   3 |    NESTED LOOPS                |                        |  3430 |   154K| 11174  (38)| 00:02:15 ||   4 |     INDEX FAST FULL SCAN       | HOBASEINFO_STATINFO_FK |  2016K|    11M|  1609   (7)| 00:00:20 ||*  5 |     TABLE ACCESS BY INDEX ROWID| T_HO_ORDER_INFO        |     1 |    40 |     1   (0)| 00:00:01 ||*  6 |      INDEX UNIQUE SCAN         | PK_T_HO_ORDER_INFO     |     1 |       |     1   (0)| 00:00:01 ||*  7 |    INDEX UNIQUE SCAN           | PK_T_SD_GUARANTY_TYPE  |     1 |     3 |     1   (0)| 00:00:01 |---------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   5 - filter("O"."RESERV_TIME">=TO_DATE('2012-01-10 00:00:00', 'yyyy-mm-dd hh24:mi:ss') AND              "O"."GUEST_NAME" LIKE U'%\738B\634D\660E%' AND "O"."RESERV_TIME"<=TO_DATE('2012-05-24 19:00:00',              'yyyy-mm-dd hh24:mi:ss') AND "O"."EMPLOYEE_ID" IS NOT NULL)   6 - access("OS"."ORDER_ID"="O"."ORDER_ID")   7 - access("O"."GUARANTY_TYPE_ID"="GT"."GUARANTY_TYPE_ID"(+)) 23 rows selected注意:HOBASEINFO_STATINFO_FK为表t_Ho_Order_Statistics上的索引,问题比较明显,嵌套循环驱动表出来了2064384行,下面虽然是通过索引唯一扫描访问t_ho_order_info但是这个操作的次数实在是太多了,肯定慢。

 

--<2>还是让它走嵌套循环,换个驱动表,48秒左右跑完SQL> explain plan for     Select Count(*)  From (Select /*+  leading(o,Os) use_nl(Os,o)  */         o.Order_Id,         o.Order_Version,         o.Guest_Name,         o.Order_Status_Id,         o.Order_Flow_Id,         o.Order_Occupt_Type_Id,         o.Pymt_Means_Id,         o.Contact_Name,         o.Reserv_Time,         h.Hotel_Name,         h.Address,         Arrival_Time,         Checkout_Time,         o.Use_Rule,         o.Guaranty_Type_Id,         Gt.Guaranty_Type_Desc,         o.Hbeorder_Id,         o.Order_Occupt_Type_Id Producttype,         o.Order_Type_Rule,         o.Hotel_Belong          From t_Ho_Order_Info o          Left Join t_Sd_Guaranty_Type Gt            On o.Guaranty_Type_Id = Gt.Guaranty_Type_Id         Inner Join t_Hp_Hotel h            On o.Hotel_Id = h.Hotel_Id         Inner Join t_Ho_Order_Statistics Os            On Os.Order_Id = o.Order_Id         Inner Join t_s_Employee e            On e.Employee_Id = o.Employee_Id         Where 1 = 1           And o.Guest_Name Like '%王明%'           And o.Reserv_Time >=               To_Date('2012-1-10 0:00:00', 'yyyy-mm-ddhh24-mi-ss')           And o.Reserv_Time <=               To_Date('2012-5-24 19:00:00', 'yyyy-mm-ddhh24-mi-ss')) T0 Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT------------------------------------------------------------------------------------------------  Plan hash value: 3583136125-----------------------------------------------------------------------------------------------| Id  | Operation            | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |-----------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT     |                        |     1 |    49 | 19342   (4)| 00:03:53 ||   1 |  SORT AGGREGATE      |                        |     1 |    49 |            |          ||   2 |   NESTED LOOPS OUTER |                        |  3430 |   164K| 19342   (4)| 00:03:53 ||   3 |    NESTED LOOPS      |                        |  3430 |   154K| 19341   (4)| 00:03:53 ||*  4 |     TABLE ACCESS FULL| T_HO_ORDER_INFO        |  2962 |   115K| 17262   (5)| 00:03:28 ||*  5 |     INDEX RANGE SCAN | HOBASEINFO_STATINFO_FK |     1 |     6 |     1   (0)| 00:00:01 ||*  6 |    INDEX UNIQUE SCAN | PK_T_SD_GUARANTY_TYPE  |     1 |     3 |     1   (0)| 00:00:01 |-----------------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   4 - filter("O"."RESERV_TIME">=TO_DATE('2012-01-10 00:00:00', 'yyyy-mm-dd              hh24:mi:ss') AND "O"."GUEST_NAME" LIKE U'%\738B\634D\660E%' AND              "O"."RESERV_TIME"<=TO_DATE('2012-05-24 19:00:00', 'yyyy-mm-dd hh24:mi:ss') AND              "O"."EMPLOYEE_ID" IS NOT NULL)   5 - access("OS"."ORDER_ID"="O"."ORDER_ID")   6 - access("O"."GUARANTY_TYPE_ID"="GT"."GUARANTY_TYPE_ID"(+)) 23 rows selected注意:这里如果单写use_nl(Os,o)并不是指Os表和o表做嵌套循环,而是指Os会用嵌套循环,o表也会用嵌套循环,加了leading之后才是把他们两个连到一起来了。Reference上的解释:Multi-table hints are like single-table hints, except that the hint can specifyone or more tables or views. LEADING is an example of a multi-table hint. Notethat USE_NL(table1 table2) is not considered a multi-table hint because it is actually a shortcut for USE_NL(table1) and USE_NL(table2). the LEADING hint specifies the exact join order to be used; the join methods to be used on the different tables are also specified.


--<3>通过加hints让其走hash,差不多40秒左右跑完 SQL> explain plan for  Select Count(*)  From (Select /*+ use_hash(Os,o) */         o.Order_Id,         o.Order_Version,         o.Guest_Name,         o.Order_Status_Id,         o.Order_Flow_Id,         o.Order_Occupt_Type_Id,         o.Pymt_Means_Id,         o.Contact_Name,         o.Reserv_Time,         h.Hotel_Name,         h.Address,         Arrival_Time,         Checkout_Time,         o.Use_Rule,         o.Guaranty_Type_Id,         Gt.Guaranty_Type_Desc,         o.Hbeorder_Id,         o.Order_Occupt_Type_Id Producttype,         o.Order_Type_Rule,         o.Hotel_Belong          From t_Ho_Order_Info o         Inner Join t_Sd_Guaranty_Type Gt            On o.Guaranty_Type_Id = Gt.Guaranty_Type_Id         Inner Join t_Hp_Hotel h            On o.Hotel_Id = h.Hotel_Id         Inner Join t_Ho_Order_Statistics Os            On Os.Order_Id = o.Order_Id         Inner Join t_s_Employee e            On e.Employee_Id = o.Employee_Id         Where 1 = 1           And o.Guest_Name Like '%王明%'           And o.Reserv_Time >=               To_Date('2012-1-10 0:00:00', 'yyyy-mm-ddhh24-mi-ss')           And o.Reserv_Time <=               To_Date('2012-5-24 19:00:00', 'yyyy-mm-ddhh24-mi-ss')) T0Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT--------------------------------------------------------------------------------------------------- Plan hash value: 3811067636-------------------------------------------------------------------------------------------------| Id  | Operation              | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |-------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT       |                        |     1 |    46 | 18941   (5)| 00:03:48 ||   1 |  SORT AGGREGATE        |                        |     1 |    46 |            |          ||*  2 |   HASH JOIN            |                        |  3430 |   154K| 18941   (5)| 00:03:48 ||*  3 |    TABLE ACCESS FULL   | T_HO_ORDER_INFO        |  2962 |   115K| 17262   (5)| 00:03:28 ||   4 |    INDEX FAST FULL SCAN| HOBASEINFO_STATINFO_FK |  2016K|    11M|  1609   (7)| 00:00:20 |-------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   2 - access("OS"."ORDER_ID"="O"."ORDER_ID")   3 - filter("O"."RESERV_TIME">=TO_DATE('2012-01-10 00:00:00', 'yyyy-mm-dd hh24:mi:ss')              AND "O"."GUEST_NAME" LIKE U'%\738B\634D\660E%' AND              "O"."RESERV_TIME"<=TO_DATE('2012-05-24 19:00:00', 'yyyy-mm-dd hh24:mi:ss') AND              "O"."EMPLOYEE_ID" IS NOT NULL AND "O"."GUARANTY_TYPE_ID" IS NOT NULL) 20 rows selected -----注意:这里将hints的写法单单改成/*+  leading(o,OS)*/,它也选择了走hash,且执行计划和上面一模一样。直接加leading,直接加leading的意思就是表名让他们两个表相连接,没有指定是Hash还是嵌套的话oracle会自行选择。 

--<4>直接加rule,12秒出结果SQL> explain plan for    Select /*+ RULE */ Count(*)  From (Select o.Order_Id,               o.Order_Version,               o.Guest_Name,               o.Order_Status_Id,               o.Order_Flow_Id,               o.Order_Occupt_Type_Id,               o.Pymt_Means_Id,               o.Contact_Name,               o.Reserv_Time,               h.Hotel_Name,               h.Address,               Arrival_Time,               Checkout_Time,               o.Use_Rule,               o.Guaranty_Type_Id,               Gt.Guaranty_Type_Desc,               o.Hbeorder_Id,               o.Order_Occupt_Type_Id Producttype,               o.Order_Type_Rule,               o.Hotel_Belong          From t_Ho_Order_Info o         Inner Join t_Sd_Guaranty_Type Gt            On o.Guaranty_Type_Id = Gt.Guaranty_Type_Id         Inner Join t_Hp_Hotel h            On o.Hotel_Id = h.Hotel_Id                 Inner Join t_Ho_Order_Statistics Os            On Os.Order_Id = o.Order_Id         Inner Join t_s_Employee e            On e.Employee_Id = o.Employee_Id         Where 1 = 1           And o.Guest_Name Like '%王明%'           And o.Reserv_Time >=               To_Date('2012-1-10 0:00:00', 'yyyy-mm-ddhh24-mi-ss')           And o.Reserv_Time <=               To_Date('2012-5-24 19:00:00', 'yyyy-mm-ddhh24-mi-ss')) T0Explained SQL> select * from table(dbms_xplan.display);PLAN_TABLE_OUTPUT---------------------------------------------------------------- Plan hash value: 4158354473----------------------------------------------------------------| Id  | Operation                     | Name                   |----------------------------------------------------------------|   0 | SELECT STATEMENT              |                        ||   1 |  SORT AGGREGATE               |                        ||   2 |   NESTED LOOPS                |                        ||*  3 |    TABLE ACCESS BY INDEX ROWID| T_HO_ORDER_INFO        ||*  4 |     INDEX RANGE SCAN          | IDX_RESERV_TIME        ||*  5 |    INDEX RANGE SCAN           | HOBASEINFO_STATINFO_FK |----------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   3 - filter("O"."GUARANTY_TYPE_ID" IS NOT NULL AND "O"."EMPLOYEE_ID"              IS NOT NULL AND "O"."GUEST_NAME" LIKE U'%\738B\634D\660E%')   4 - access("O"."RESERV_TIME">=TO_DATE('2012-01-10 00:00:00',              'yyyy-mm-dd hh24:mi:ss') AND "O"."RESERV_TIME"<=TO_DATE('2012-05-24              19:00:00', 'yyyy-mm-dd hh24:mi:ss'))   5 - access("OS"."ORDER_ID"="O"."ORDER_ID")Note-----   - rule based optimizer used (consider using cbo) 26 rows selected

注意:加了rule之后,是通过索引范围扫描访问了t_ho_order_info,Reserv_Time列上的索

引IDX_RESERV_TIME起效果了?关注一下这个索引。

--先前原始SQL走t_ho_order_info上面的PK_T_HO_ORDER_INFO,是因为嵌套循环
可以通过等值条件使用索引,而非使用到了谓词过滤条件上面的索引列
-----------------------------------
Select c.Table_Name  表名,
       a.Index_Name  索引名,
       a.index_type  索引类型,
       b.Column_Name 列名,
       a.Distinct_Keys     列中不同的值,
       a.Clustering_Factor As 集群因子,
       a.Last_Analyzed     As 索引上次统计信息收集时间,
       c.Last_Analyzed     As 表上次统计信息收集时间
  From User_Indexes a, User_Ind_Columns b, User_Tables c
 Where a.Index_Name = b.Index_Name
   And a.Table_Name = c.Table_Name
   And a.Index_Name = Upper('IDX_RESERV_TIME');
表名               索引名            索引类型    列名           列中不同的值    集群因子    索引上次统计信息收集时间    表上次统计信息收集时间
T_HO_ORDER_INFO     IDX_RESERV_TIME    NORMAL     RESERV_TIME    1763955         1699935     2012/3/28 17:17:05            2012/3/28 17:16:21
------------------------------------
SQL>analyze index IDX_RESERV_TIME validate Structure
SQL>select lf_rows,lf_rows_len,del_lf_rows,del_lf_rows_len from index_stats;

LF_ROWS    LF_ROWS_LEN    DEL_LF_ROWS    DEL_LF_ROWS_LEN

2064512    39225728      31495          598405
碎片貌似有点多

在线rebuild一下
SQL>alter index IDX_RESERV_TIME rebuild Online
SQL>analyze index IDX_RESERV_TIME validate Structure
SQL>select lf_rows,lf_rows_len,del_lf_rows,del_lf_rows_len from index_stats;
LF_ROWS    LF_ROWS_LEN    DEL_LF_ROWS    DEL_LF_ROWS_LEN
2033136    38629584      0              0

SQL>exec dbms_stats.gather_index_stats('gc','IDX_RESERV_TIME');
------------------------------------
将索引做了调整后,发现原SQL仍然是原来那种效果,没有改进
看来还是集群因子的问题


--<5>
根据rule的思路,我们直接强制走索引IDX_RESERV_TIME,0.3秒出结果SQL> explain plan forSelect Count(*)  From (Select /*+ index(o IDX_RESERV_TIME)*/         o.Order_Id,         o.Order_Version,         o.Guest_Name,         o.Order_Status_Id,         o.Order_Flow_Id,         o.Order_Occupt_Type_Id,         o.Pymt_Means_Id,         o.Contact_Name,         o.Reserv_Time,         h.Hotel_Name,         h.Address,         Arrival_Time,         Checkout_Time,         o.Use_Rule,         o.Guaranty_Type_Id,         Gt.Guaranty_Type_Desc,         o.Hbeorder_Id,         o.Order_Occupt_Type_Id Producttype,         o.Order_Type_Rule,         o.Hotel_Belong          From t_Ho_Order_Info o          Left Join t_Sd_Guaranty_Type Gt            On o.Guaranty_Type_Id = Gt.Guaranty_Type_Id         Inner Join t_Hp_Hotel h            On o.Hotel_Id = h.Hotel_Id         Inner Join t_Ho_Order_Statistics Os            On Os.Order_Id = o.Order_Id         Inner Join t_s_Employee e            On e.Employee_Id = o.Employee_Id         Where 1 = 1           And o.Guest_Name Like '%王明%'           And o.Reserv_Time >=               To_Date('2012-1-10 0:00:00', 'yyyy-mm-ddhh24-mi-ss')           And o.Reserv_Time <=               To_Date('2012-5-24 19:00:00', 'yyyy-mm-ddhh24-mi-ss')) T0   ; Explained SQL> select * from table(dbms_xplan.display); PLAN_TABLE_OUTPUT----------------------------------------------------------------------------------------------------------- Plan hash value: 3515346792---------------------------------------------------------------------------------------------------------| Id  | Operation                      | Name                   | Rows  | Bytes | Cost (%CPU)| Time     |---------------------------------------------------------------------------------------------------------|   0 | SELECT STATEMENT               |                        |     1 |    49 | 19878   (2)| 00:03:59 ||   1 |  SORT AGGREGATE                |                        |     1 |    49 |            |          ||*  2 |   HASH JOIN                    |                        |  3430 |   164K| 19878   (2)| 00:03:59 ||   3 |    NESTED LOOPS OUTER          |                        |  2962 |   124K| 18199   (1)| 00:03:39 ||*  4 |     TABLE ACCESS BY INDEX ROWID| T_HO_ORDER_INFO        |  2962 |   115K| 18198   (1)| 00:03:39 ||*  5 |      INDEX RANGE SCAN          | IDX_RESERV_TIME        | 63952 |       |    62   (4)| 00:00:01 ||*  6 |     INDEX UNIQUE SCAN          | PK_T_SD_GUARANTY_TYPE  |     1 |     3 |     1   (0)| 00:00:01 ||   7 |    INDEX FAST FULL SCAN        | HOBASEINFO_STATINFO_FK |  2016K|    11M|  1609   (7)| 00:00:20 |---------------------------------------------------------------------------------------------------------Predicate Information (identified by operation id):---------------------------------------------------   2 - access("OS"."ORDER_ID"="O"."ORDER_ID")   4 - filter("O"."GUEST_NAME" LIKE U'%\738B\634D\660E%' AND "O"."EMPLOYEE_ID" IS NOT NULL)   5 - access("O"."RESERV_TIME">=TO_DATE('2012-01-10 00:00:00', 'yyyy-mm-dd hh24:mi:ss') AND              "O"."RESERV_TIME"<=TO_DATE('2012-05-24 19:00:00', 'yyyy-mm-dd hh24:mi:ss'))   6 - access("O"."GUARANTY_TYPE_ID"="GT"."GUARANTY_TYPE_ID"(+)) 23 rows selected