no_merge优化MERGE JOIN CARTESIAN跑不出结果到1S

开发说下面这个sql语句跑不出结果

SELECT
     t1.order_id,

     t2.order_name,

     t1.order_flow_no,

     t1.order_type,

     t1.agent_id,

     t1.money,

     t1.order_create_time

      FROM (SELECT  re.id AS order_id,

                   re.serialnumber AS order_flow_no,

                   re.money AS money,

                   ‘1‘ AS order_type,

                   re.agent_id AS agent_id,

                   re.create_date AS order_create_time

              FROM tb_recharge re

             WHERE create_date >=

                   to_date(‘2014-07-08 00:00:00‘, ‘yyyy-mm-dd hh24:mi:ss‘) -

                   INTERVAL ‘1‘

             DAY

               AND create_date <

                   to_date(‘2014-07-09 00:00:00‘, ‘yyyy-mm-dd hh24:mi:ss‘)

               AND re.status != 2) t1

      LEFT JOIN (SELECT  tt1.orderform_flow_no, tt1.order_name

                   FROM (SELECT tp1.PAYMENT_FLOW_NO,

                                tp1.orderform_flow_no,

                                tp1.orderform_name AS order_name

                           FROM tb_payment tp1

                          WHERE create_time >=

                                to_date(‘2014-07-08 00:00:00‘,

                                        ‘yyyy-mm-dd hh24:mi:ss‘) - INTERVAL ‘1‘

                          DAY

                            AND create_time <

                                to_date(‘2014-07-09 00:00:00‘,

                                        ‘yyyy-mm-dd hh24:mi:ss‘)

                            AND tp1.orderform_type = 1

                            AND tp1.id NOT IN

                                (SELECT tr.payment_id FROM tb_refund tr)) tt1

                   LEFT JOIN (SELECT 
                              business_flow_no

                               FROM tb_fund_flow f

                              WHERE f.business_type = 0

                                AND create_time >= to_date(‘2014-07-08 00:00:00‘,

                                                           ‘yyyy-mm-dd hh24:mi:ss‘) -

                                    INTERVAL ‘1‘ DAY

                                AND create_time <

                                    to_date(‘2014-07-09 00:00:00‘,

                                            ‘yyyy-mm-dd hh24:mi:ss‘)) tt2

                     ON tt1.PAYMENT_FLOW_NO = tt2.business_flow_no

                  WHERE tt2.business_flow_no IS NOT NULL) t2

        ON t1.order_flow_no = t2.orderform_flow_no

     WHERE t2.orderform_flow_no IS NOT NULL

plan如下

--------------------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name   | Rows  | Bytes | Cost (%CPU)| Time  | Pstart| Pstop |
--------------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT         |   |     1 |   440 |   158K (75)| 00:31:40 |  |  |
|*  1 |  HASH JOIN ANTI          |   |     1 |   440 |   158K (75)| 00:31:40 |  |  |
|   2 |   NESTED LOOPS          |   |     1 |   434 |   156K (75)| 00:31:23 |  |  |
|   3 |    NESTED LOOPS          |   |     1 |   434 |   156K (75)| 00:31:23 |  |  |
|   4 |     MERGE JOIN CARTESIAN        |   |     1 |   125 |   156K (75)| 00:31:23 |  |  |
|   5 |      PARTITION RANGE SINGLE        |   |     1 |    83 |     0   (0)| 00:00:01 |    12 |    12 |
|*  6 |       TABLE ACCESS BY LOCAL INDEX ROWID| TB_RECHARGE  |     1 |    83 |     0   (0)| 00:00:01 |    12 |    12 |
|*  7 |        INDEX RANGE SCAN         | TB_RECHARGE_I5  |     1 |  |     0   (0)| 00:00:01 |    12 |    12 |
|   8 |      BUFFER SORT         |   |   673K|    26M|   156K (75)| 00:31:23 |  |  |
|   9 |       PARTITION RANGE SINGLE        |   |   673K|    26M|   156K (75)| 00:31:23 |     8 |     8 |
|* 10 |        TABLE ACCESS FULL        | TB_FUND_FLOW  |   673K|    26M|   156K (75)| 00:31:23 |     8 |     8 |
|* 11 |     INDEX UNIQUE SCAN         | UNIQ_FLOW_TYPE1 |     1 |  |     2   (0)| 00:00:01 |  |  |
|* 12 |    TABLE ACCESS BY GLOBAL INDEX ROWID  | TB_PAYMENT  |     1 |   309 |     2   (0)| 00:00:01 |     8 |     8 |
|  13 |   TABLE ACCESS FULL         | TB_REFUND  |   150K|   884K|  1402   (1)| 00:00:17 |  |  |
--------------------------------------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   1 - access("TP1"."ID"="TR"."PAYMENT_ID")
   6 - filter("RE"."STATUS"<>2)
   7 - access("CREATE_DATE">=TO_DATE(‘ 2014-07-07 00:00:00‘, ‘syyyy-mm-dd hh24:mi:ss‘) AND
       "CREATE_DATE"<TO_DATE(‘ 2014-07-09 00:00:00‘, ‘syyyy-mm-dd hh24:mi:ss‘))
  10 - filter("F"."BUSINESS_TYPE"=0 AND "CREATE_TIME">=TO_DATE(‘ 2014-07-07 00:00:00‘, ‘syyyy-mm-dd hh24:mi:ss‘)
       AND "CREATE_TIME"<TO_DATE(‘ 2014-07-09 00:00:00‘, ‘syyyy-mm-dd hh24:mi:ss‘) AND "BUSINESS_FLOW_NO" IS NOT NULL)
  11 - access("RE"."SERIALNUMBER"="TP1"."ORDERFORM_FLOW_NO" AND "TP1"."ORDERFORM_TYPE"=1)
       filter("TP1"."ORDERFORM_FLOW_NO" IS NOT NULL)
  12 - filter("CREATE_TIME">=TO_DATE(‘ 2014-07-07 00:00:00‘, ‘syyyy-mm-dd hh24:mi:ss‘) AND
       "CREATE_TIME"<TO_DATE(‘ 2014-07-09 00:00:00‘, ‘syyyy-mm-dd hh24:mi:ss‘) AND
       "TP1"."PAYMENT_FLOW_NO"="BUSINESS_FLOW_NO")

相关表大小说明:

tb_recharge: 6G

tb_fund_flow: 9G

tb_payment: 4G

都为分区表

这个执行计划一眼就可以看出很多表统计信息有问题,先不管统计信息是否正确,定位到id为4 中tb_recharge 和tb_fund_folw  两个大表做MERGE JOIN CARTESIAN   笛卡尔,并且tb_found_flow  TABLE FULL SCAN, 发现问题的时候这个sql已经跑了3个小时了。

注意上面sql语句的写法,都是将大表先过滤在相互去join,说明一点这个sql最终join结果只有2条数据。但是最终CBO查询改写了,导致内敛视图合并,出现笛卡尔。看到这里优化就非常简单了,于是我加了一个hint no_merge不允许视图合并。

下面是最后的结果,1s数据就出来了

SELECT

    /*+ no_merge(t1) no_merge(t2)*/

     t1.order_id,

     t2.order_name,

     t1.order_flow_no,

     t1.order_type,

     t1.agent_id,

     t1.money,

     t1.order_create_time

      FROM (SELECT re.id AS order_id,

                   re.serialnumber AS order_flow_no,

                   re.money AS money,

                   ‘1‘ AS order_type,

                   re.agent_id AS agent_id,

                   re.create_date AS order_create_time

              FROM tb_recharge re

             WHERE create_date >=

                   to_date(‘2014-07-08 00:00:00‘, ‘yyyy-mm-dd hh24:mi:ss‘) -

                   INTERVAL ‘1‘

             DAY

               AND create_date <

                   to_date(‘2014-07-09 00:00:00‘, ‘yyyy-mm-dd hh24:mi:ss‘)

               AND re.status != 2) t1

      LEFT JOIN (SELECT  tt1.orderform_flow_no, tt1.order_name

                   FROM (SELECT tp1.PAYMENT_FLOW_NO,

                                tp1.orderform_flow_no,

                                tp1.orderform_name AS order_name

                           FROM tb_payment tp1

                          WHERE create_time >=

                                to_date(‘2014-07-08 00:00:00‘,

                                        ‘yyyy-mm-dd hh24:mi:ss‘) - INTERVAL ‘1‘

                          DAY

                            AND create_time <

                                to_date(‘2014-07-09 00:00:00‘,

                                        ‘yyyy-mm-dd hh24:mi:ss‘)

                            AND tp1.orderform_type = 1

                            AND tp1.id NOT IN

                                (SELECT tr.payment_id FROM tb_refund tr)) tt1

                   LEFT JOIN (SELECT /*+ index(f IDX_FUNDFLOW_CRTTIME) */

                              business_flow_no

                               FROM tb_fund_flow f

                              WHERE f.business_type = 0

                                AND create_time >= to_date(‘2014-07-08 00:00:00‘,

                                                           ‘yyyy-mm-dd hh24:mi:ss‘) -

                                    INTERVAL ‘1‘ DAY

                                AND create_time <

                                    to_date(‘2014-07-09 00:00:00‘,

                                            ‘yyyy-mm-dd hh24:mi:ss‘)) tt2

                     ON tt1.PAYMENT_FLOW_NO = tt2.business_flow_no

                  WHERE tt2.business_flow_no IS NOT NULL) t2

        ON t1.order_flow_no = t2.orderform_flow_no

     WHERE t2.orderform_flow_no IS NOT NULL

注意由于tb_fund_flow 是按月分区表,这里查询一天的数据,必然走index比较快,可见统计信息错误,由于系统所有sql语句都在1秒内出结果,我没有去收集统计信息,于是加了这个hint  /*+ index(f IDX_FUNDFLOW_CRTTIME) */ ,下面是最终的执行计划

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

| Id  | Operation          | Name        | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     | Pstart| Pstop |

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

|   0 | SELECT STATEMENT         |        |     1 |   327 |       |   415K (1)| 01:23:07 |       |       |

|*  1 |  HASH JOIN          |        |     1 |   327 |       |   415K (1)| 01:23:07 |       |       |

|   2 |   PARTITION RANGE SINGLE        |        |     1 |    73 |       |     0 (0)| 00:00:01 |    12 |    12 |

|   3 |    VIEW           |        |     1 |    73 |       |     0 (0)| 00:00:01 |       |       |

|*  4 |     TABLE ACCESS BY LOCAL INDEX ROWID  | TB_RECHARGE       |     1 |    83 |       |     0 (0)| 00:00:01 |    12 |    12 |

|*  5 |      INDEX RANGE SCAN         | TB_RECHARGE_I5       |     1 |       |       |     0 (0)| 00:00:01 |    12 |    12 |

|   6 |   VIEW           |        |  6740 |  1671K|       |   415K (1)| 01:23:07 |       |       |

|*  7 |    HASH JOIN          |        |  6740 |  2349K|       |   415K (1)| 01:23:07 |       |       |

|*  8 |     HASH JOIN RIGHT ANTI        |        |   990 |   304K|  2656K| 41160 (1)| 00:08:14 |       |       |

|   9 |      TABLE ACCESS FULL         | TB_REFUND       |   150K|   884K|       |  1402 (1)| 00:00:17 |       |       |

|  10 |      PARTITION RANGE SINGLE        |        | 98977 |    29M|       | 38125 (1)| 00:07:38 |     8 |     8 |

|* 11 |       TABLE ACCESS BY LOCAL INDEX ROWID| TB_PAYMENT       | 98977 |    29M|       | 38125 (1)| 00:07:38 |     8 |     8 |

|* 12 |        INDEX RANGE SCAN         | IDX_PAYMENT_CRTTIME  | 98977 |       |       |  1550 (1)| 00:00:19 |     8 |     8 |

|  13 |     PARTITION RANGE SINGLE        |        |   673K|    26M|       |   374K (1)| 01:14:53 |     8 |     8 |

|* 14 |      TABLE ACCESS BY LOCAL INDEX ROWID | TB_FUND_FLOW       |   673K|    26M|       |   374K (1)| 01:14:53 |     8 |     8 |

|* 15 |       INDEX RANGE SCAN         | IDX_FUNDFLOW_CRTTIME |   697K|       |       | 12504 (1)| 00:02:31 |     8 |     8 |

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

添加了no_merge Hint后可以看到id等于3和6出现了view关键字,达到了最后sql写法的目的,先过滤大数据在join,最后这个sql 1s出了结果。开发傻眼了

no_merge优化MERGE JOIN CARTESIAN跑不出结果到1S

时间: 2024-10-28 10:33:42

no_merge优化MERGE JOIN CARTESIAN跑不出结果到1S的相关文章

统计信息不准导致执行计划出错跑不出结果,优化后只要1分钟

一天查看数据库长会话,发现1个sql跑得很慢,1个多小时不出结果,花了点时间把它给优化了. 优化前: SELECT 20131023, "A2"."ORG_ID", COUNT(DISTINCT NLSSORT(CASE "A2"."RES_TYPE" WHEN 'DP' THEN "A2"."RES_CODE" END, 'nls_sort=''BINARY''')), COUNT(D

Sort merge join、Nested loops、Hash join(三种连接类型)

目前为止,典型的连接类型有3种: Sort merge join(SMJ排序-合并连接): 首先生产driving table需要的数据,然后对这些数据按照连接操作关联列进行排序:然后生产probed table需要的数据,然后对这些数据按照与driving table对应的连接操作列进行排序:最后两边已经排序的行被放在一起执行合并操作.排序是一个费时.费资源的操作,特别对于大表.所以smj通常不是一个特别有效的连接方法,但是如果driving table和probed table都已经预先排序

NESTED LOOPS &amp; HASH JOIN &amp; SORT MERGE JOIN

表连接方式及使用场合 NESTED LOOP 对于被连接的数据子集较小的情况,nested loop连接是个较好的选择.nested loop就是扫描一个表,每读到一条记录,就根据索引去另一个表里面查找,没有索引一般就不会是 nested loops.一般在nested loop中, 驱动表满足条件结果集不大,被驱动表的连接字段要有索引,这样就走nstedloop.如果驱动表返回记录太多,就不适合nested loops了.如果连接字段没有索引,则适合走hash join,因为不需要索引. 可用

多表连接的三种方式详解 HASH JOIN MERGE JOIN NESTED LOOP

在多表联合查询的时候,如果我们查看它的执行计划,就会发现里面有多表之间的连接方式. 之前打算在sqlplus中用执行计划的,但是格式看起来有点乱,就用Toad 做了3个截图. 从3张图里我们看到了几点信息: 1.       CBO 使用的ALL_ROWS模式 Oracle Optimizer CBO RBO http://blog.csdn.NET/tianlesoftware/archive/2010/08/19/5824886.aspx 2.       表之间的连接用了hash Join

深入理解Oracle表(3):三大表连接方式详解之Nested loop join和 Sort merge join

深入理解Oracle表(3):三大表连接方式详解之Nested loop join和 Sort merge join 分类: Oracle 基础管理 Oracle SQL 开发2013-01-28 00:33 2536人阅读 评论(1) 收藏 举报 关系数据库技术的精髓就是通过关系表进行规范化的数据存储       并通过各种表连接技术和各种类型的索引技术来进行信息的检索和处理       这里Think愿意和大家一起来学习分享Oracle的三大表连接技术              在早期版本,

Nested Loops,Hash Join , Sort Merge Join

在多表联合查询的时候,如果我们查看它的执行计划,就会发现里面有多表之间的连接方式.多表之间的连接有三种方式:Nested Loops,Hash Join 和 Sort Merge Join.具体适用哪种类型的连接取决于 当前的优化器模式 (ALL_ROWS 和 RULE) 取决于表大小 取决于连接列是否有索引 取决于连接列是否排序 下面来介绍三种不同连接工作方式的不同: 实验sql 假如有10000个城市,对应于10个国家(此例子仅仅可以解释join工作的过程) 更换优化器,添加索引,会影响下面

oracle多表连接方式Hash Join Nested Loop Join Merge Join

在查看sql执行计划时,我们会发现表的连接方式有多种,本文对表的连接方式进行介绍以便更好看懂执行计划和理解sql执行原理. 一.连接方式:        嵌套循环(Nested  Loops (NL))      (散列)哈希连接(Hash Join (HJ))    (归并)排序合并连接(Sort Merge Join (SMJ) ) 二.连接说明:    1.Oracle一次只能连接两个表.不管查询中有多少个表,Oracle 在连接中一次仅能操作两张表.    2.当执行多个表的连接时,优化

mysql 如何优化left join

今天遇到一个left join优化的问题,搞了一下午,中间查了不少资料,对MySQL的查询计划还有查询优化有了更进一步的了解,做一个简单的记录: select c.* from hotel_info_original c left join hotel_info_collection h on c.hotel_type=h.hotel_type and c.hotel_id =h.hotel_id where h.hotel_id is null 这个sql是用来查询出c表中有h表中无的记录,所

Sql Server 优化 SQL 查询:如何写出高性能SQL语句

1. 首先要搞明白什么叫执行计划? 执行计划是数据库根据SQL语句和相关表的统计信息作出的一个查询方案,这个方案是由查询优化器自动分析产生的,比如一条SQL语句如果用来从一个 10万条记录的表中查1条记录,那查询优化器会选择“索引查找”方式,如果该表进行了归档,当前只剩下5000条记录了,那查询优化器就会改变方案,采用 “全表扫描”方式. 可见,执行计划并不是固定的,它是“个性化的”.产生一个正确的“执行计划”有两点很重要: (1)    SQL语句是否清晰地告诉查询优化器它想干什么? (2)