该sql如下:

Select  /*+ parallel(src, 8) */ distinct

src.systemname as systemname

,  src.databasename as databasename

,  src.tablename as tablename

,  src.username as username

from  meta_dbql_table_usage_exp_hst src

inner join DR_QRY_LOG_EXP_HST rl on

src.acctstringdate = rl.acctstringdate

and src.queryid = rl.queryid

And Src.Systemname = Rl.Systemname

and src.acctstringdate > sysdate - 30

And Rl.Acctstringdate > Sysdate - 30

inner join  meta_dr_qry_log_tgt_all_hst tgt on

upper(tgt.systemname) = upper('MOZART')

And Upper(tgt.Databasename) = Upper('GDW_TABLES')

And Upper(tgt.Tablename) = Upper('SSA_SLNG_LSTG_MTRC_SD')

AND src.acctstringdate = tgt.acctstringdate

and rl.statement_id = tgt.statement_id

and rl.systemname = tgt.systemname

And Tgt.Acctstringdate > Sysdate - 30

And Not(

Upper(Tgt.Systemname)=Upper(src.systemname)

And

Upper(Tgt.Databasename) = Upper(Src.Databasename)

And

Upper(Tgt.Tablename) = Upper(Src.Tablename)

)

And   tgt.Systemname is not null

And   tgt.Databasename Is Not Null

And   tgt.tablename is not null

SQL的简单分析总得来看,这个SQL就是三个表(meta_dbql_table_usage_exp_hst,DR_QRY_LOG_EXP_HST,meta_dr_qry_log_tgt_all_hst)的INNER JOIN,这三个表数据量都在百万级别,且都是分区表(以acctstringdate为分区键),执行计划如下:

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

| Id  | Operation                              | Name                          | Rows  | Bytes | Cost  | Pstart| Pstop |

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

|   0 | SELECT STATEMENT                       |                               |     1 |   159 |  8654 |       |       |

|   1 |  PX COORDINATOR                        |                               |       |       |       |       |       |

|   2 |   PX SEND QC (RANDOM)                  | :TQ10002                      |     1 |   159 |  8654 |       |       |

|   3 |    SORT UNIQUE                         |                               |     1 |   159 |  8654 |       |       |

|   4 |     PX RECEIVE                         |                               |     1 |    36 |     3 |       |       |

|   5 |      PX SEND HASH                      | :TQ10001                      |     1 |    36 |     3 |       |       |

|*  6 |       TABLE ACCESS BY LOCAL INDEX ROWID| DR_QRY_LOG_EXP_HST            |     1 |    36 |     3 |       |       |

|   7 |        NESTED LOOPS                    |                               |     1 |   159 |  8633 |       |       |

|   8 |         NESTED LOOPS                   |                               |  8959 |  1076K|  4900 |       |       |

|   9 |          BUFFER SORT                   |                               |       |       |       |       |       |

|  10 |           PX RECEIVE                   |                               |       |       |       |       |       |

|  11 |            PX SEND BROADCAST           | :TQ10000                      |       |       |       |       |       |

|  12 |             PARTITION RANGE ITERATOR   |                               |     1 |    56 |  4746 |   KEY |    14 |

|* 13 |              TABLE ACCESS FULL         | META_DR_QRY_LOG_TGT_ALL_HST   |     1 |    56 |  4746 |   KEY |    14 |

|  14 |          PX BLOCK ITERATOR             |                               |  8959 |   586K|   154 |   KEY |   KEY |

|* 15 |           TABLE ACCESS FULL            | META_DBQL_TABLE_USAGE_EXP_HST |  8959 |   586K|   154 |   KEY |   KEY |

|  16 |         PARTITION RANGE ITERATOR       |                               |     1 |       |     2 |   KEY |   KEY |

|* 17 |          INDEX RANGE SCAN              | DR_QRY_LOG_EXP_HST_IDX        |     1 |       |     2 |   KEY |   KEY |

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

Predicate Information (identified by operation id):

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

6 - filter("RL"."STATEMENT_ID"="TGT"."STATEMENT_ID" AND "RL"."SYSTEMNAME"="TGT"."SYSTEMNAME" AND "SRC"."SYSTEMNAME"="RL"."SYSTEMNAME")

13 - filter(UPPER("TGT"."SYSTEMNAME")='MOZART' AND UPPER("TGT"."DATABASENAME")='GDW_TABLES' AND

UPPER("TGT"."TABLENAME")='SSA_SLNG_LSTG_MTRC_SD' AND "TGT"."ACCTSTRINGDATE">SYSDATE@!-30 AND "TGT"."SYSTEMNAME" IS NOT NULL

"TGT"."DATABASENAME" IS NOT NULL AND "TGT"."TABLENAME" IS NOT NULL)

15 - filter("SRC"."ACCTSTRINGDATE"="TGT"."ACCTSTRINGDATE" AND (UPPER("TGT"."SYSTEMNAME")<>UPPER("SRC"."SYSTEMNAME") OR

UPPER("TGT"."DATABASENAME")<>UPPER("SRC"."DATABASENAME") OR UPPER("TGT"."TABLENAME")<>UPPER("SRC"."TABLENAME")) AND

"SRC"."ACCTSTRINGDATE">SYSDATE@!-30)

17 - access("SRC"."QUERYID"="RL"."QUERYID" AND "SRC"."ACCTSTRINGDATE"="RL"."ACCTSTRINGDATE")

filter("RL"."ACCTSTRINGDATE">SYSDATE@!-30)

定位问题从上面执行计划中的表连接方式可以知道,这三个表之间进行了两次NESTED LOOP,问题出现在最里层的NESTED LOOP(对两个表都做了TABLE FULL SCAN),因为表都是百万级别的(即时过滤后的数据量也不小),性能问题就出现在内表(即被驱动表)META_DBQL_TABLE_USAGE_EXP_HST做了太多次的全表扫描。如果能把全表扫描转换成索引,则性能可以大幅度提高。

下面是NESTED LOOP的介绍:嵌套连接把要处理的数据集分为外部循环(驱动数据源)和内部循环(被驱动数据源),外部循环只执行一次,内部循环执行的次数等于外部循环执行返回的数据个数。

这种连接的好处是内存使用非常少。

如果驱动数据源有限,且被驱动表在连接列上有相应的索引,则这种连接方式才是高效的。

下面是这三个表上索引的情况:

SQL> select index_name, table_name from user_indexes where table_name in ('DR_QRY_LOG_EXP_HST',upper('meta_dbql_table_usage_exp_hst'), upper('meta_dr_qry_log_tgt_all_hs

INDEX_NAME                                                   TABLE_NAME

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

META_DR_QRY_LOG_TGT_ALL_IDX                                  META_DR_QRY_LOG_TGT_ALL_HST

META_DBQL_TUSAGE_EHST_IDX                                    META_DBQL_TABLE_USAGE_EXP_HST

DR_QRY_LOG_EXP_HST_IDX                                       DR_QRY_LOG_EXP_HST

CREATE INDEX "GV"."META_DR_QRY_LOG_TGT_ALL_IDX" ON "GV"."META_DR_QRY_LOG_TGT_ALL_HST" ("STATEMENT_ID", "ACCTSTRINGDATE")

CREATE INDEX "GV"."META_DBQL_TUSAGE_EHST_IDX" ON "GV"."META_DBQL_TABLE_USAGE_EXP_HST" ("QUERYID", "ACCTSTRINGDATE")

CREATE INDEX "GV"."DR_QRY_LOG_EXP_HST_IDX" ON "GV"."DR_QRY_LOG_EXP_HST" ("QUERYID", "ACCTSTRINGDATE")

这三个索引都是本地分区索引(都包含分区键acctstringdate),很显然,DR_QRY_LOG_EXP_HST表少了个索引,因为它与表meta_dr_qry_log_tgt_all_hst 在statement_id上做join,因此应该在它的statement_id上也创建本地分区索引如下:

create index DR_QRY_LOG_EXP_HST_IDX2 on gv.DR_QRY_LOG_EXP_HST (statement_id,ACCTSTRINGDATE) local;

性能对比新的执行计划如下:

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

| Id  | Operation                              | Name                          | Rows  | Bytes | Cost  | Pstart| Pstop |

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

|   0 | SELECT STATEMENT                       |                               |     1 |   159 |  4838 |       |       |

|   1 |  SORT UNIQUE                           |                               |     1 |   159 |  4838 |       |       |

|*  2 |   TABLE ACCESS BY LOCAL INDEX ROWID    | META_DBQL_TABLE_USAGE_EXP_HST |     1 |    67 |     3 |       |       |

|   3 |    NESTED LOOPS                        |                               |     1 |   159 |  4816 |       |       |

|   4 |     NESTED LOOPS                       |                               |    18 |  1656 |  4762 |       |       |

|   5 |      PARTITION RANGE ITERATOR          |                               |     1 |    56 |  4746 |   KEY |    14 |

|*  6 |       TABLE ACCESS FULL                | META_DR_QRY_LOG_TGT_ALL_HST   |     1 |    56 |  4746 |   KEY |    14 |

|   7 |      PARTITION RANGE ITERATOR          |                               |    18 |   648 |    16 |   KEY |    14 |

|*  8 |       TABLE ACCESS BY LOCAL INDEX ROWID| DR_QRY_LOG_EXP_HST            |    18 |   648 |    16 |   KEY |    14 |

|*  9 |        INDEX RANGE SCAN                | DR_QRY_LOG_EXP_HST_IDX2       |    31 |       |    15 |   KEY |    14 |

|  10 |     PARTITION RANGE ITERATOR           |                               |     1 |       |     2 |   KEY |   KEY |

|* 11 |      INDEX RANGE SCAN                  | META_DBQL_TUSAGE_EHST_IDX     |     1 |       |     2 |   KEY |   KEY |

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

Predicate Information (identified by operation id):

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

2 - filter((UPPER("TGT"."SYSTEMNAME")<>UPPER("SRC"."SYSTEMNAME") OR

UPPER("TGT"."DATABASENAME")<>UPPER("SRC"."DATABASENAME") OR UPPER("TGT"."TABLENAME")<>UPPER("SRC"."TABLENAME"))

AND "SRC"."SYSTEMNAME"="RL"."SYSTEMNAME")

6 - filter(UPPER("TGT"."SYSTEMNAME")='MOZART' AND UPPER("TGT"."DATABASENAME")='GDW_TABLES' AND

UPPER("TGT"."TABLENAME")='SSA_SLNG_LSTG_MTRC_SD' AND "TGT"."ACCTSTRINGDATE">SYSDATE@!-30 AND "TGT"."SYSTEMNAME"

IS NOT NULL AND "TGT"."DATABASENAME" IS NOT NULL AND "TGT"."TABLENAME" IS NOT NULL)

8 - filter("RL"."SYSTEMNAME"="TGT"."SYSTEMNAME")

9 - access("RL"."STATEMENT_ID"="TGT"."STATEMENT_ID" AND "RL"."ACCTSTRINGDATE">SYSDATE@!-30 AND

"RL"."ACCTSTRINGDATE" IS NOT NULL)

11 - access("SRC"."QUERYID"="RL"."QUERYID" AND "SRC"."ACCTSTRINGDATE"="RL"."ACCTSTRINGDATE")

filter("SRC"."ACCTSTRINGDATE"="TGT"."ACCTSTRINGDATE" AND "SRC"."ACCTSTRINGDATE">SYSDATE@!-30)

从新的的执行计划可以看出,它的第一个NESTED LOOP果然用了最新创建的索引。

下面是执行时间:

已用时间:  00: 00: 02.16

两秒种搞定,远远超出他期望的5s :)

方法总结NESTED LOOP高效的条件:驱动数据源有限,且被驱动表在连接列上有相应的索引。

oracle全局索引改成本地索引,解析一个通过添加本地分区索引提高SQL性能的案例...相关推荐

  1. [Oracle] 一个通过添加本地分区索引提高SQL性能的案例

    今天接到同事求助,说有一个select query,在Oracle上要跑一分多钟,他希望能在5s内出结果,该sql如下: Select /*+ parallel(src, 8) */ distinct ...

  2. 一个通过添加本地分区索引提高SQL性能的案例

    今天接到同事求助,说有一个select query,在Oracle上要跑一分多钟,他希望能在5s内出结果,该sql如下: [sql]  view plain copy print ? Select   ...

  3. 【Oracle】表级别分区操作对索引(本地分区索引,全局分区索引,非分区索引)的影响

    --参考自<Oracle索引技术> 先贴上结论吧: 下面对以上几种操作分别测试: 创建测试表及索引 --创建测试分区表 CREATE TABLE employees_parttest (e ...

  4. oracle全局索引改成本地索引,FAQ : 如何获得 Oracle 分区索引的类型 - 全局分区索引、本地分区索引...

    Oracle 数据库针对分区的信息,通过多个数据字典视图来维护,所以在获取信息时,经常会困惑 DBA 们. 例如,如何获取 分区索引的类型,如何判断一个索引,是 全局分区索引,还是本地分区索引? 通过 ...

  5. oracle分区索引及循环插入

    表可以按range.hash.list分区,表分区后,其上的索引和普通表上的索引有所不同,oracle对于分区表上的索引分为2类,即局部索引和全局索引,下面分别对这2种索引的特点和局限性做个总结. 局 ...

  6. Oracle分区表及分区索引

    Oracle分区索引 索引与表类似,也可以分区: 分区索引分为两类: Locally partitioned index(局部分区索引) Globally partitioned index(全局分区 ...

  7. mysql 分区 全局索引_全局分区索引与局部分区索引

    分区索引 分区索引,有是全局分区索引与局部分区索引,加上一种全局非分区索引(也就是普通索引),加起来共三种.下面我们讨论了这三种索引的组织结构以及应用场景. 1.全局非分区索引可以依赖普通的表,也可以 ...

  8. oracle list 分区详解,oracle的List分区及分区索引

    最近在做的一个项目,由于涉及到数据量会在半年内破千万条,考虑到数据量比较大,日后查询起来会比较费时,所以考虑使用oracle的分区特性.oracle10g产品中的分区表分为List,hash,rang ...

  9. 如何获得Oracle分区索引类型

    碰巧在墨天轮上看资料就看到了eygle的这篇文章<如何获得 Oracle 分区索引的类型 - 全局分区索引.本地分区索引>,秉承了eygle大神一如既往的风格,文章"短小&quo ...

最新文章

  1. BERT是图像预训练未来?字节跳动iBOT刷新十几项SOTA,部分指标超MAE
  2. LeetCode 226. Invert Binary Tree--反转二叉树--C++,Python解法--递归,迭代做法
  3. js转换html为pdf文件怎么打开方式,pdf.js实现在HTML下直接浏览pdf文档,无需插件即可实现...
  4. 设计模式 — 行为型模式 — 策略模式
  5. 如何胜任一个小型公司的技术总监?我的感想
  6. 快速搭建CentOS+ASP.NET Core环境支持WebSocket
  7. JavaScript循环:标签语句,继续语句和中断语句说明
  8. 怎么把jad反编译放到Eclipse中
  9. 如何把 .mobi 格式的电子书转换成 PDF
  10. 【深入理解Java】一篇文章带你彻底吃透Java NIO
  11. Adobe Dreamweaver的使用教程
  12. finecms存在任意文件上传漏洞复现
  13. WimTool安装使用方法
  14. 2023 IEEE Fellow出炉:唐立新、宗成庆、朱军、姬水旺、李佳等入选
  15. html页面日期显示带0,XHTML1.0与HTML兼容指引16条 小结
  16. 关闭windows server 2012 IE增强的安全配置
  17. efm32芯片电压_小壁虎PK小蝴蝶(EFM32 vs STM32L)
  18. 2021年危险化学品经营单位主要负责人考试报名及危险化学品经营单位主要负责人证考试
  19. beyond compare 3中文乱码
  20. 这五大采购流程最需要实现自动化

热门文章

  1. Photoshop | 快速抠头发(调整边缘/选择并遮住)
  2. Jupyter notebook中自定义支持天软TSl语言的魔术命令
  3. linux装回win10系统无法开机,Win10/Linux双系统删除之后出现grub无法开机修复方法...
  4. SpringDataJPA教程 三种配置方式
  5. 母牛生小牛问题-字节跳动笔试题
  6. Asuswrt RT-AC68U 华硕路由器文件删除漏洞 栈溢出
  7. python打气球小游戏(一)
  8. Python基础知识——函数的基本使用、函数的参数、名称空间与作用域、函数对象与闭包、 装饰器、迭代器、生成器与yield、函数递归、面向过程与函数式(map、reduce、filter)
  9. 马斯克等超1200人联名公开信呼吁:停止训练更强大的AI,这背后原因发人深省,该给ChatGPT泼冷水了
  10. opencv-车辆牌照识别(一)