常用的几种Hints优化一条SQL

环境: 
oracle 10g R2 
Select Count(*) From t_Ho_Order_Statistics --2032946
Select Count(*) From t_Ho_Order_Info       --2032946
其他都是小的维度表
统计信息已经检查过了,差不多10天前的(不过我10天前跑过这个SQL,出来的执行计划一样),
这里,这里就把注意力集中在两个大表连接的问题上,

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

文章主要是指出hints的基本用法,还有使用hints的误区
--<1>

下面这条SQL要运行5分钟左右,出来的结果为0 

原SQL和执行计划

SQL> explain plan for
Select 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')) T0
Explained

 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 specify
one or more tables or views. LEADING is an example of a multi-table hint. Note
that 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')) T0

Explained

 

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')) T0


Explained

 
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 for
Select 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




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值