大表关联走hash?
案例:

----    反正我执行过1个多小时,没有跑完

执行计划:

1 Plan hash value: 309883988
2  
3 --------------------------------------------------------------------------------------------------------------------------------------------
4 | Id  | Operation                                 | Name                   | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     | Pstart| Pstop |
5 --------------------------------------------------------------------------------------------------------------------------------------------
6 |   0 | SELECT STATEMENT                          |                        |     1 |   130 |       |  1218K  (1)| 04:03:44 |       |       |
7 |*  1 |  FILTER                                   |                        |       |       |       |            |          |       |       |
8 |   2 |   NESTED LOOPS                            |                        | 21005 |  2666K|       |  1166K  (1)| 03:53:19 |       |       |
9 |   3 |    NESTED LOOPS                           |                        | 21791 |  2666K|       |  1166K  (1)| 03:53:19 |       |       |
10|*  4 |     HASH JOIN                             |                        | 21791 |  2234K|    17M|  1079K  (1)| 03:35:53 |       |       |
11|   5 |      NESTED LOOPS                         |                        |   183K|    15M|       |   907K  (1)| 03:01:32 |       |       |
12|   6 |       NESTED LOOPS                        |                        |   183K|    15M|       |   907K  (1)| 03:01:32 |       |       |
13|   7 |        NESTED LOOPS                       |                        |   183K|    11M|       |   358K  (1)| 01:11:37 |       |       |
14|*  8 |         TABLE ACCESS BY GLOBAL INDEX ROWID| EM_ORDER               |   106K|  3631K|       | 39284   (1)| 00:07:52 |     8 |     8 |
15|*  9 |          INDEX RANGE SCAN                 | IDX_EM_ORDER_COMP_TIME | 44669 |       |       |   330   (0)| 00:00:04 |       |       |
16|* 10 |         INDEX RANGE SCAN                  | IDX_ER_ORDER_ORDER_OO  |     2 |    56 |       |     3   (0)| 00:00:01 |       |       |
17|* 11 |        INDEX UNIQUE SCAN                  | PK_EM_ORDER            |     1 |       |       |     2   (0)| 00:00:01 |       |       |
18|  12 |       TABLE ACCESS BY GLOBAL INDEX ROWID  | EM_ORDER               |     1 |    26 |       |     3   (0)| 00:00:01 | ROWID | ROWID |
19|  13 |      PARTITION RANGE ALL                  |                        |    10M|   161M|       |   156K  (1)| 00:31:22 |     1 |    10 |
20|* 14 |       TABLE ACCESS FULL                   | EE_ORDER_PF_WORK       |    10M|   161M|       |   156K  (1)| 00:31:22 |     1 |    10 |
21|* 15 |     INDEX RANGE SCAN                      | IDX_101_T_ID           |     1 |       |       |     3   (0)| 00:00:01 |       |       |
22|  16 |    TABLE ACCESS BY GLOBAL INDEX ROWID     | M_101_ID_2_GID         |     1 |    25 |       |     4   (0)| 00:00:01 | ROWID | ROWID |
23|  17 |   TABLE ACCESS BY INDEX ROWID             | DM_STAFF               |     1 |    20 |       |     3   (0)| 00:00:01 |       |       |
24|* 18 |    INDEX UNIQUE SCAN                      | PK_DM_STAFF            |     1 |       |       |     2   (0)| 00:00:01 |       |       |
25--------------------------------------------------------------------------------------------------------------------------------------------
26   
27  Predicate Information (identified by operation id):
28  ---------------------------------------------------
29   
30     1 - filter( (SELECT "F_CHK_IDCARD"("X"."IDENTITY_NUMBER") FROM "QWZW_ER"."DM_STAFF" "X" WHERE "X"."ID"=:B1)=0)
31     4 - access("D"."ID"="E"."ORDER_ID")
32     8 - filter("A"."SPEC_ID"=3010200004 AND "A"."STATUS_ID"=1000007)
33     9 - access("A"."COMPLETE_TIME">=TO_DATE(' 2016-11-16 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND "A"."COMPLETE_TIME"<=TO_DATE(' 
34                2016-11-16 23:59:59', 'syyyy-mm-dd hh24:mi:ss'))
35         filter(TBL$OR$IDX$PART$NUM("QWZW_ER"."EM_ORDER",0,1,0,ROWID)=8)
36    10 - access("A"."ID"="C"."A_ORDER_ID")
37    11 - access("C"."B_ORDER_ID"="D"."ID")
38    14 - filter("E"."WORK_TYPE_ID"=1001411)
39    15 - access("A"."ID"="B"."T_ID")
40         filter("B"."T_ID" IS NOT NULL)
41    18 - access("X"."ID"=:B1)


--------------------------------------------------------------------------------------------------------------------------------------------

问题分析:
   1,看执行计划:
      第一眼看见这个sql我会认为id=7到id=9有问题,id=14有问题。

     id=9,表40G,虽然一个分区但是全局索引,索引很大的;大表回表再过滤是有问题的,看9的过滤条件,
    查询的是一天的数据,这个表是按月分区的,存储一年的数据,
 
   40/365=0.1G,也就是说每天0.1g,所以这里问题不大。
   但还是建本地索引速度会更快一点。建的索引不合理,况且这个影响不是很大,暂且先忽略。

      看id=14,E表,4G的表,全表扫描很慢, 很多人想到能不能建索引? 答案是不能!  "E"."WORK_TYPE_ID"=1001411数据很多,建索引也不走,就算走了索引估计还没全表快。
    id=14和id=15走hash,有问题,E表4g的表,全表扫描 , 肯定需要大量时间。 


      我第一眼看到id=1 的地方 filter,谓词部分 后面有一个小表的查询, 这个地方肯定有性能问题的,于是在整个SQL优化完后, 我把这个filter去掉,居然发现查询时间差不多
     于是我就没有管它。   


   2,看sql:这是5个表关联,看where条件部分,E表4g比较大,而过滤条件只有一个 e.work_type_id = 1001411,
      你看这个SQL发现 E表的数据不再 select 后面的列出现,可以改成半连接的,而且半连接的效率要高一点。    于是我改成exists
 改写为
      exists( select   1 from   EE_ORDER_PF_WORK E  where   D.ID = E.ORDER_ID   AND e.work_type_id = 1001411 ) . 
    其实更多的时候我会改成in。 


   3  加hint 使执行计划总体走NL,这里让小表作为驱动表,走NL,一路驱动下去。为什么select这里要全走NL?因为看上面的执行计划已经分析
      过了id=5到id=12虽然都是大表,   但是走的都是索引, 结合A表时间条件, 以及其他条件, 驱动表的数据量不是很多。 并且最终数据量也不是很多。
       

  改SQL:为   


4,改写之后的执行计划:此时id=14已经走了NESTED LOOPS SEMI,而id=13已经自动走了索引。
 

1 Plan hash value: 1576200999
2  
3 -----------------------------------------------------------------------------------------------------------------------------------
4 | Id  | Operation                                | Name                   | Rows  | Bytes | Cost (%CPU)| Time     | Pstart| Pstop |
5 -----------------------------------------------------------------------------------------------------------------------------------
6 |   0 | SELECT STATEMENT                         |                        |  3537 |   449K|  1372K  (1)| 04:34:26 |       |       |
7 |*  1 |  FILTER                                  |                        |       |       |            |          |       |       |
8 |   2 |   NESTED LOOPS                           |                        | 19483 |  2473K|  1372K  (1)| 04:34:26 |       |       |
9 |   3 |    NESTED LOOPS                          |                        | 20212 |  2473K|  1372K  (1)| 04:34:26 |       |       |
10  |   4 |     NESTED LOOPS SEMI                    |                        | 20212 |  2072K|  1291K  (1)| 04:18:16 |       |       |
11  |   5 |      NESTED LOOPS                        |                        |   169K|    14M|   844K  (1)| 02:48:57 |       |       |
12  |   6 |       NESTED LOOPS                       |                        |   169K|    10M|   334K  (1)| 01:07:00 |       |       |
13  |*  7 |        TABLE ACCESS BY GLOBAL INDEX ROWID| EM_ORDER               | 98540 |  3368K| 39284   (1)| 00:07:52 |     8 |     8 |
14  |*  8 |         INDEX RANGE SCAN                 | IDX_EM_ORDER_COMP_TIME | 44669 |       |   330   (0)| 00:00:04 |       |       |
15  |*  9 |        INDEX RANGE SCAN                  | IDX_ER_ORDER_ORDER_OO  |     2 |    56 |     3   (0)| 00:00:01 |       |       |
16  |  10 |       TABLE ACCESS BY GLOBAL INDEX ROWID | EM_ORDER               |     1 |    26 |     3   (0)| 00:00:01 | ROWID | ROWID |
17  |* 11 |        INDEX UNIQUE SCAN                 | PK_EM_ORDER            |     1 |       |     2   (0)| 00:00:01 |       |       |
18  |* 12 |      TABLE ACCESS BY GLOBAL INDEX ROWID  | EE_ORDER_PF_WORK       |  1257K|    19M|     3   (0)| 00:00:01 | ROWID | ROWID |
19  |* 13 |       INDEX UNIQUE SCAN                  | PK_EE_ORDER_PF_WORK    |     1 |       |     2   (0)| 00:00:01 |       |       |
20  |* 14 |     INDEX RANGE SCAN                     | IDX_101_T_ID           |     1 |       |     3   (0)| 00:00:01 |       |       |
21  |  15 |    TABLE ACCESS BY GLOBAL INDEX ROWID    | M_101_ID_2_GID         |     1 |    25 |     4   (0)| 00:00:01 | ROWID | ROWID |
22  |  16 |   TABLE ACCESS BY INDEX ROWID            | DM_STAFF               |     1 |    20 |     3   (0)| 00:00:01 |       |       |
23  |* 17 |    INDEX UNIQUE SCAN                     | PK_DM_STAFF            |     1 |       |     2   (0)| 00:00:01 |       |       |
24  -----------------------------------------------------------------------------------------------------------------------------------
25   
26  Predicate Information (identified by operation id):
27  ---------------------------------------------------
28   
29     1 - filter("D"."DEAL_OPER_ID" IS NULL OR  (SELECT "F_CHK_IDCARD"("X"."IDENTITY_NUMBER") FROM "QWZW_ER"."DM_STAFF" "X" 
30                WHERE "X"."ID"=:B1)=0)
31     7 - filter("A"."SPEC_ID"=3010200004 AND "A"."STATUS_ID"=1000007)
32     8 - access("A"."COMPLETE_TIME">=TO_DATE(' 2016-11-18 00:00:00', 'syyyy-mm-dd hh24:mi:ss') AND 
33                "A"."COMPLETE_TIME"<=TO_DATE(' 2016-11-18 23:59:59', 'syyyy-mm-dd hh24:mi:ss'))
34         filter(TBL$OR$IDX$PART$NUM("QWZW_ER"."EM_ORDER",0,1,0,ROWID)=8)
35     9 - access("A"."ID"="C"."A_ORDER_ID")
36    11 - access("C"."B_ORDER_ID"="D"."ID")
37    12 - filter("E"."WORK_TYPE_ID"=1001411)
38    13 - access("D"."ID"="E"."ORDER_ID")
39    14 - access("A"."ID"="B"."T_ID")
40         filter("B"."T_ID" IS NOT NULL)
41    17 - access("X"."ID"=:B1)

   
5,优化完之后, 首次执行大概在 4分钟吧,   再次执行只需要 47S 左右。对比之前的执行计划,反正木有发现在47S出结果的。   结果大概3千多条数据。
   总结:大表很大表关联,常理思路是走hash,  但是此处场景,驱动表通过过滤之后返回几千条数据,被驱动表走主键唯一扫描,走NL效率更高。

01-20 19:00