【性能调优】记一次性能调优问题的讨论(用到了TEMPORARY TABLE)


昨天在和论坛上一位朋友讨论一条update SQL的性能优化时,聊到了Oracle的TEMPORARY TABLE,感觉在某些情况下还是有些用的,这里备忘一下。


那位朋友的问题:
--------------------------------------

這段SQL能否能優化,實在太慢了 

UPDATE HG_JHPC t
     SET (WGSJ) = (select sum(t_qty)
                     from (SELECT MSI.SEGMENT1 ITEM,
                                  to_char(MT.TRANSACTION_DATE, 'yyyy-mm-dd') T_date,
                                  MT.TRANSACTION_QUANTITY T_QTY
                             FROM INV.MTL_MATERIAL_TRANSACTIONS MT,
                                  INV.MTL_SYSTEM_ITEMS_B        MSI
                            WHERE MT.INVENTORY_ITEM_ID = MSI.INVENTORY_ITEM_ID
                              and MT.TRANSACTION_DATE >= sysdate - 31
                              and msi.segment1 not like 'FA%'
                              and mt.transaction_type_id in (17, 44)
                              AND MT.SUBINVENTORY_CODE = 'F010'
                           UNION ALL
                           select MSI.SEGMENT1 ITEM,
                                  TO_CHAR(T.TRANSACTION_DATE, 'YYYY-MM-DD') T_date,
                                  T.TRANSACTION_QUANTITY T_QTY
                             from inv.mtl_material_transactions t,
                                  INV.MTL_SYSTEM_ITEMS_B        MSI
                            WHERE T.INVENTORY_ITEM_ID(+) =
                                  MSI.INVENTORY_ITEM_ID
                              and T.TRANSACTION_DATE >= sysdate - 31
                              and msi.segment1 not like 'FA%'
                              AND T.TRANSFER_SUBINVENTORY IN ('R010', 'R041')
                              AND T.TRANSACTION_TYPE_ID = 2
                              AND T.SUBINVENTORY_CODE = 'F010')
                    WHERE substr(ITEM, 3, 6) = substr(t.ITEM, 3, 6)
                      and T_DATE >= '2011-11-01'
                      AND T_DATE <= '2011-11-05'
                      and TO_CHAR(t.PC_DATE, 'YYYY-MM-DD') = '2011-11-06' --TO_CHAR(SYSDATE+1,'YYYY-MM-DD')
                      and SUBSTR(t.ZB_CLASS, 1, 2) = 'HX'
                    group by substr(item, 3, 6))
   where exists
   (select sum(t_qty)
            from (SELECT MSI.SEGMENT1 ITEM,
                         to_char(MT.TRANSACTION_DATE, 'yyyy-mm-dd') T_date,
                         MT.TRANSACTION_QUANTITY T_QTY
                    FROM INV.MTL_MATERIAL_TRANSACTIONS MT,
                         INV.MTL_SYSTEM_ITEMS_B        MSI
                   WHERE MT.INVENTORY_ITEM_ID = MSI.INVENTORY_ITEM_ID
                     and MT.TRANSACTION_DATE >= sysdate - 31
                     and msi.segment1 not like 'FA%'
                     and mt.transaction_type_id in (17, 44)
                     AND MT.SUBINVENTORY_CODE = 'F010'
                  UNION ALL
                  select MSI.SEGMENT1 ITEM,
                         TO_CHAR(T.TRANSACTION_DATE, 'YYYY-MM-DD') T_date,
                         T.TRANSACTION_QUANTITY T_QTY
                    from inv.mtl_material_transactions t,
                         INV.MTL_SYSTEM_ITEMS_B        MSI
                   WHERE T.INVENTORY_ITEM_ID(+) = MSI.INVENTORY_ITEM_ID
                     and T.TRANSACTION_DATE >= sysdate - 31
                     and msi.segment1 not like 'FA%'
                     AND T.TRANSFER_SUBINVENTORY IN ('R010', 'R041')
                     AND T.TRANSACTION_TYPE_ID = 2
                     AND T.SUBINVENTORY_CODE = 'F010')
           WHERE substr(ITEM, 3, 6) = substr(t.ITEM, 3, 6)
             and T_DATE >= '2011-11-01'
             AND T_DATE <= '2011-11-05'
             and TO_CHAR(t.PC_DATE, 'YYYY-MM-DD') = '2011-11-06'--TO_CHAR(SYSDATE+1,'YYYY-MM-DD')
             and SUBSTR(t.ZB_CLASS, 1, 2) = 'HX'
           group by substr(item, 3, 6));

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


经询问后得知:
---------------------------------------
(select sum(t_qty)
                     from (SELECT MSI.SEGMENT1 ITEM,
                                  to_char(MT.TRANSACTION_DATE, 'yyyy-mm-dd') T_date,
                                  MT.TRANSACTION_QUANTITY T_QTY
                             FROM INV.MTL_MATERIAL_TRANSACTIONS MT,
                                  INV.MTL_SYSTEM_ITEMS_B        MSI
                            WHERE MT.INVENTORY_ITEM_ID = MSI.INVENTORY_ITEM_ID
                              and MT.TRANSACTION_DATE >= sysdate - 31
                              and msi.segment1 not like 'FA%'
                              and mt.transaction_type_id in (17, 44)
                              AND MT.SUBINVENTORY_CODE = 'F010'
                           UNION ALL
                           select MSI.SEGMENT1 ITEM,
                                  TO_CHAR(T.TRANSACTION_DATE, 'YYYY-MM-DD') T_date,
                                  T.TRANSACTION_QUANTITY T_QTY
                             from inv.mtl_material_transactions t,
                                  INV.MTL_SYSTEM_ITEMS_B        MSI
                            WHERE T.INVENTORY_ITEM_ID(+) =
                                  MSI.INVENTORY_ITEM_ID
                              and T.TRANSACTION_DATE >= sysdate - 31
                              and msi.segment1 not like 'FA%'
                              AND T.TRANSFER_SUBINVENTORY IN ('R010', 'R041')
                              AND T.TRANSACTION_TYPE_ID = 2
                              AND T.SUBINVENTORY_CODE = 'F010')

這段和 where exists
後面的括號裏面的是一樣的,


(SELECT MSI.SEGMENT1 ITEM,
                                  to_char(MT.TRANSACTION_DATE, 'yyyy-mm-dd') T_date,
                                  MT.TRANSACTION_QUANTITY T_QTY
                             FROM INV.MTL_MATERIAL_TRANSACTIONS MT,
                                  INV.MTL_SYSTEM_ITEMS_B        MSI
                            WHERE MT.INVENTORY_ITEM_ID = MSI.INVENTORY_ITEM_ID
                              and MT.TRANSACTION_DATE >= sysdate - 31
                              and msi.segment1 not like 'FA%'
                              and mt.transaction_type_id in (17, 44)
                              AND MT.SUBINVENTORY_CODE = 'F010'
                           UNION ALL
                           select MSI.SEGMENT1 ITEM,
                                  TO_CHAR(T.TRANSACTION_DATE, 'YYYY-MM-DD') T_date,
                                  T.TRANSACTION_QUANTITY T_QTY
                             from inv.mtl_material_transactions t,
                                  INV.MTL_SYSTEM_ITEMS_B        MSI
                            WHERE T.INVENTORY_ITEM_ID(+) =
                                  MSI.INVENTORY_ITEM_ID
                              and T.TRANSACTION_DATE >= sysdate - 31
                              and msi.segment1 not like 'FA%'
                              AND T.TRANSFER_SUBINVENTORY IN ('R010', 'R041')
                              AND T.TRANSACTION_TYPE_ID = 2
                              AND T.SUBINVENTORY_CODE = 'F010')
這段查出來的結果當做一張表
---------------------------------------

由于这位朋友发帖时没有提供相关几个表的表定义SQL文,尤其是索引和主键的定义信息,也没有提供复杂查询现在的执行计划,所以调优一时无法下手。
昨天当时又赶上加班没太多时间考虑,所以简单分析下后,给这位朋友两个提案。
-----------------------------------------
先提供两个提案,楼主可以先试一下
1、做一个临时表(※1),并给这个临时表上加上合适的索引。  执行update前,先将共通子查询那部分数据先灌入临时表,然后再执行update。
※1:我指的是创建Oracle中的TEMPORARY TABLE对象(语法:CREATE TEMPORARY TABLE tblname。。。。),语法可以到网上查一下。

(SELECT MSI.SEGMENT1 ITEM,
                                  to_char(MT.TRANSACTION_DATE, 'yyyy-mm-dd') T_date,
                                  MT.TRANSACTION_QUANTITY T_QTY
                             FROM INV.MTL_MATERIAL_TRANSACTIONS MT,
                                  INV.MTL_SYSTEM_ITEMS_B        MSI
                            WHERE MT.INVENTORY_ITEM_ID = MSI.INVENTORY_ITEM_ID
                              and MT.TRANSACTION_DATE >= sysdate - 31
                              and msi.segment1 not like 'FA%'
                              and mt.transaction_type_id in (17, 44)
                              AND MT.SUBINVENTORY_CODE = 'F010'
                           UNION ALL
                           select MSI.SEGMENT1 ITEM,
                                  TO_CHAR(T.TRANSACTION_DATE, 'YYYY-MM-DD') T_date,
                                  T.TRANSACTION_QUANTITY T_QTY
                             from inv.mtl_material_transactions t,
                                  INV.MTL_SYSTEM_ITEMS_B        MSI
                            WHERE T.INVENTORY_ITEM_ID(+) =
                                  MSI.INVENTORY_ITEM_ID
                              and T.TRANSACTION_DATE >= sysdate - 31
                              and msi.segment1 not like 'FA%'
                              AND T.TRANSFER_SUBINVENTORY IN ('R010', 'R041')
                              AND T.TRANSACTION_TYPE_ID = 2
                              AND T.SUBINVENTORY_CODE = 'F010')

2、分析上面SQL文的执行计划,然后在合适的地方 使用Oracle Hint,人为地改变 SQL文的执行计划。
关于获取SQL文执行计划的方法  和 Oracle Hint的语法,网上很多,楼主可以试着百度或谷歌一下。

以上两种方案相结合,相信会有一定帮助。
------------------------------------------

后来,又将这个问题中如何创建和使用Temporary table给这位朋友简单写了个例子。
--------------------------------
是像下面这样来做。

■1、创建一个辅助的临时表tt1,以得到 要建的临时表 的各个字段的类型和长度信息。

SQL> create table tt1 as
  2  select * from dept;

Table created

SQL> desc tt1
Name   Type         Nullable Default Comments 
------ ------------ -------- ------- -------- 
DEPTNO NUMBER(2)    Y                         
DNAME  VARCHAR2(14) Y                         
LOC    VARCHAR2(13) Y                         

SQL> 


■2、删除掉辅助表tt1。
SQL> drop table tt1 purge;

Table dropped

SQL> 


■3、创建临时表。
SQL> CREATE GLOBAL TEMPORARY TABLE GBL_TMP_TBL
  2  (
  3    DEPTNO NUMBER(2),
  4    DNAME  VARCHAR2(14),
  5    LOC    VARCHAR2(13)
  6  ) ON COMMIT PRESERVE ROWS;

Table created

这里有个地方需要注意一下,请根据应用需求选择使用。
既然是临时表,顾名思义,存放在该表中的数据是临时性的。ORACLE根据你创建临时表时指定的参数(On Commit Delete Rows / On Commit Preserve Rows),自动将数据TRUNCATE掉。
(1)On Commit Delete Rows : 数据在 Transaction 期间有效,一旦COMMIT后,数据就被自动 TRUNCATE 掉了;
(2)On Commit Preserve Rows :数据在 Session 期间有效,一旦关闭了Session 或 Log Off 后,数据就被 ORACLE 自动 Truncate 掉。
这里我随便选了一个。

■4、根据需要给临时表追加索引。
SQL> create index idx_GBL_TMP_TBL_1 on GBL_TMP_TBL(DEPTNO);

Index created


至此临时表就建好了。


下面,是向临时表中插入数据的方法,加在你的update语句前面即可。

■5、向临时表中插入数据。

SQL> insert into GBL_TMP_TBL
  2  select * from dept;

4 rows inserted

SQL> select * from  GBL_TMP_TBL;

DEPTNO DNAME          LOC
------ -------------- -------------
    10 ACCOUNTING     NEW YORK
    20 RESEARCH       DALLAS
    30 SALES          CHICAGO
    40 OPERATIONS     BOSTON

SQL>
--------------------------------

最后,和另一位发帖的朋友简单分享了一下对这条SQL文调优的一些看法。
-------------------------
其实楼主的这个SQL文的调优困难的最大问题是这条SQL文太复杂了,
而且从各块SQL文来说,可调优的地方有限,最多加个Hint。

这条SQL文想要提高执行效率,恐怕只有改写,
1、要么向上面说的尝试换用merge语法来改写一下; 
2、或者像我上面提到的那样,尝试保留现在update文逻辑的基础上,将子查询拿出来先灌入一个有索引的临时表,然后再执行update,当然相应地,这时update的写法也可以简化,并且可以尝试在适当的地方应用Hint来微调SQL文的执行计划。
感觉只有改写,才能根本提升这条SQL文的执行效率。

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

今天早上看了一下帖子,楼主朋友还没有回帖说一下问题解决的进度,上面的分析和问题解决提案如果能对楼主朋友的问题解决 能够有些帮助,就十分高兴了!希望那位朋友的问题能够早日顺利解决。 002.gif


以上。


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/20335819/viewspace-710308/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/20335819/viewspace-710308/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值