繁体   English   中英

性能调优sql查询

[英]performance tuning sql query

我有这个查询,它是性能调整的候选者,因为它需要4-5个小时才能运行。

explain plan for SELECT /*+ PARALLEL_INDEX(ssp, sub_svc_parm_ix2, 4) 
                          INDEX(ssp sub_svc_parm_ix2) */ 
             SUB_SVC_ID
        FROM SUB_SVC_PARM ssp
       WHERE PARM_ID = GET_PARM_ID('net_ppv_credit_limit', GET_CLASS_ID('SubSvcSpec'), GET_SVCID('smp_cpe_cas'))
         AND VAL <> '140.00'
         AND EXISTS (SELECT /*+ PARALLEL_INDEX(ss, sub_svc_pk, 4) */ 
                            1
                       FROM SUB_SVC ss
                      WHERE ss.SUB_SVC_ID = ssp.SUB_SVC_ID
                        AND ss.SUB_SVC_STATUS_ID NOT IN (FN_GET_STATUS_ID('SubSvcSpec', 'deleted'),
                                                         FN_GET_STATUS_ID('SubSvcSpec', 'inactive'),
                                                         FN_GET_STATUS_ID('SubSvcSpec', 'add_in_progress'),
                                                         FN_GET_STATUS_ID('SubSvcSpec', 'activation_in_progress'),
                                                         FN_GET_STATUS_ID('SubSvcSpec', 'courtesy_block_in_progress'),
                                                         FN_GET_STATUS_ID('SubSvcSpec', 'mso_block_in_progress'),
                                                         FN_GET_STATUS_ID('SubSvcSpec', 'delete_in_progress'),
                                                         FN_GET_STATUS_ID('SubSvcSpec', 'deactivation_in_progress'),
                                                         FN_GET_STATUS_ID('SubSvcSpec', 'change_in_progress')));

我在这里发布原始查询,该查询在过程中称为游标。

在耗时的域上解释查询的计划是-

Plan hash value: 4290343623

----------------------------------------------------------------------------           -----------------------------           
  | Id  | Operation                    | Name             | Rows  | Bytes      |TempSpc| Cost (%CPU)| Time     |
 ---------------------------------------------------------------------------   ------------------------------
 |   0 | SELECT STATEMENT             |                  |  1802K|    56M|       |   528K  (1)|730:11:02 |
 |*  1 |  HASH JOIN RIGHT SEMI        |                  |  1802K|    56M|    37M|   528K  (1)|730:11:02 |
 |*  2 |   TABLE ACCESS FULL          | SUB_SVC          |  1763K|    16M|       |   311K  (1)|430:15:33 |
 |   3 |   TABLE ACCESS BY INDEX ROWID| SUB_SVC_PARM     |  2394K|    52M|       |   209K  (0)|288:56:00 |
 |*  4 |    INDEX RANGE SCAN          | SUB_SVC_PARM_IX2 |  2394K|       |       |  1519   (0)| 02:05:59 |
---------------------------------------------------------------------------------------------------------

 Predicate Information (identified by operation id):
---------------------------------------------------

 1 - access("SS"."SUB_SVC_ID"="SSP"."SUB_SVC_ID")
 2 - filter("SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','deleted') AND 
          "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','inactive') AND 
          "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','add_in_progress') AND 
          "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','activation_in_progress') AND 
          "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','courtesy_block_in_progress') AND 
          "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','mso_block_in_progress') AND 
          "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','delete_in_progress') AND 
          "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','deactivation_in_progress') AND 
          "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','change_in_progress'))
4 - access("PARM_ID"="GET_PARM_ID"('net_ppv_credit_limit',"GET_CLASS_ID"('SubSvcSpec'),"GET_SV
          CID"('smp_cpe_cas')))
   filter("VAL"<>'140.00')

在两个表上建立的索引是-

SUB_SVC表

index_name      coulmn_name
-----------     ------------
SUB_SVC_PK      sub_svc_id
SUB_SVC_IX4     PARENT_SUB_SVC_ID
SUB_SVC_IX5     EXTERNAL_KEY
SUB_SVC_IX6     SUB_SVC_IX6

SUB_SVC_PARM表

index_name        coulmn_name
-----------       ------------
SUB_SVC_PARM_PK   SUB_SVC_ID, PARM_ID
SUB_SVC_PARM_IX2  PARM_ID, VAL

为SUB_SVC_PARM表创建表语法

 CREATE TABLE "SMPHOMCM"."SUB_SVC_PARM" 
   (    "SUB_SVC_ID" NUMBER(12,0) NOT NULL ENABLE, 
"PARM_ID" NUMBER(12,0) NOT NULL ENABLE, 
"VAL" VARCHAR2(2000 BYTE) NOT NULL ENABLE, 
 CONSTRAINT "SUB_SVC_PARM_PK" PRIMARY KEY ("SUB_SVC_ID", "PARM_ID")
  USING INDEX)

CREATE TABLE SYNTAX FOR SUB_SVC TABLE

CREATE TABLE "SMPHOMCM"."SUB_SVC" 
   (    "SUB_SVC_ID" NUMBER(12,0) NOT NULL ENABLE, 
"SUB_ID" NUMBER(12,0) NOT NULL ENABLE, 
"START_DT" DATE NOT NULL ENABLE, 
"EXTERNAL_KEY" VARCHAR2(100 BYTE) NOT NULL ENABLE, 
"SAMP_VER" NUMBER(9,0) NOT NULL ENABLE, 
"SUB_SVC_STATUS_ID" NUMBER(12,0) NOT NULL ENABLE, 
"CREATED_DTM" DATE NOT NULL ENABLE, 
"CREATED_BY" VARCHAR2(30 BYTE) NOT NULL ENABLE, 
"END_DT" DATE, 
"PURCHASE_DT" DATE, 
"PARENT_SUB_SVC_ID" NUMBER(12,0), 
"PRE_STATUS_ID" NUMBER(12,0), 
"MODIFIED_DTM" DATE, 
"MODIFIED_BY" VARCHAR2(30 BYTE), 
"SVC_ID" NUMBER(12,0), 
 CONSTRAINT "SUB_SVC_PK" PRIMARY KEY ("SUB_SVC_ID")
 USING INDEX)

同时,我正在尝试将WITH子句用于调用where子句的函数,但是在过程中,函数会陷入常量变量中,然后在查询中使用它们!

我的数据库是:Oracle Database 11g企业版11.2.0.2.0版-64位生产

如果需要,请询问更多信息。 谢谢

编辑:此过程正在使用查询,不是我写的! 我认为除了查询之外,此过程本身还需要调整

请建议

 CREATE OR REPLACE PROCEDURE PPV_CREDIT_LIMIT(p_exid NUMBER)
 IS
 -- Flag 'N' is null

   TYPE tab_sub_svc_id               IS TABLE OF    SUB_SVC_PARM.SUB_SVC_ID%TYPE INDEX BY PLS_INTEGER;
   rs_sub_svc_id                     tab_sub_svc_id;

   c_class_SubSvcSpec                constant pls_integer := GET_CLASS_ID('SubSvcSpec');
   c_svc_smp_cpe_cas                 constant pls_integer := GET_SVCID('smp_cpe_cas');
   c_parm_net_ppv_credit_limit       constant pls_integer := GET_PARM_ID('net_ppv_credit_limit', c_class_SubSvcSpec, c_svc_smp_cpe_cas);
   c_deleted                         constant pls_integer := FN_GET_STATUS_ID('SubSvcSpec', 'deleted');
   c_inactive                        constant pls_integer := FN_GET_STATUS_ID('SubSvcSpec', 'inactive');
   c_add_in_progress                 constant pls_integer := FN_GET_STATUS_ID('SubSvcSpec', 'add_in_progress');
   c_activation_in_progress          constant pls_integer := FN_GET_STATUS_ID('SubSvcSpec', 'activation_in_progress');
   c_courtesy_block_in_progress      constant pls_integer := FN_GET_STATUS_ID('SubSvcSpec', 'courtesy_block_in_progress');
   c_mso_block_in_progress           constant pls_integer := FN_GET_STATUS_ID('SubSvcSpec', 'mso_block_in_progress');
   c_delete_in_progress              constant pls_integer := FN_GET_STATUS_ID('SubSvcSpec', 'delete_in_progress');
   c_deactivation_in_progress        constant pls_integer := FN_GET_STATUS_ID('SubSvcSpec', 'deactivation_in_progress');
   c_change_in_progress              constant pls_integer := FN_GET_STATUS_ID('SubSvcSpec', 'change_in_progress');
   c_ppv_credit_limit                constant varchar2(6) := '140.00';
   -- Added for net_creditthreshold parm
   c_parm_ppv_credit_threshold       constant pls_integer :=   GET_PARM_ID('net_ppv_creditthreshold', c_class_SubSvcSpec, c_svc_smp_cpe_cas);
   c_ppv_credit_threshold            constant varchar2(6) := '80.00';
   ilimit                            CONSTANT PLS_INTEGER := 1000;
   iCheck                            CONSTANT PLS_INTEGER := 10;
   l_total_recs                               PLS_INTEGER;
   l_rec_cnt                                  PLS_INTEGER;
   l_curr_cnt                                 PLS_INTEGER := 0;
   l_batch                                    PLS_INTEGER := 0;
   v_stop_flag                                CHAR(1) := 'N';

   cursor curPPV_CL IS
      SELECT /*+ PARALLEL_INDEX(ssp, sub_svc_parm_ix2, 4) 
                          INDEX(ssp sub_svc_parm_ix2) */ 
             SUB_SVC_ID
        FROM SUB_SVC_PARM ssp
       WHERE PARM_ID = c_parm_net_ppv_credit_limit
         AND VAL <> '140.00'
         AND EXISTS (SELECT /*+ PARALLEL_INDEX(ss, sub_svc_pk, 4) */ 
                            1
                       FROM SUB_SVC ss
                      WHERE ss.SUB_SVC_ID = ssp.SUB_SVC_ID
                        AND ss.SUB_SVC_STATUS_ID NOT IN (c_deleted,
                                                         c_inactive,
                                                         c_add_in_progress,
                                                         c_activation_in_progress,
                                                         c_courtesy_block_in_progress,
                                                         c_mso_block_in_progress,
                                                         c_delete_in_progress,
                                                         c_deactivation_in_progress,
                                                         c_change_in_progress));

BEGIN

   DBMS_APPLICATION_INFO.set_action (NULL);
   DBMS_APPLICATION_INFO.set_module (NULL, NULL);
   DBMS_APPLICATION_INFO.set_client_info (NULL);

   DBMS_APPLICATION_INFO.set_module (module_name => 'Procedure: PPV_CREDIT_LIMIT',
                                 action_name => 'Counting total updatable records');

   SELECT /*+ PARALLEL_INDEX(ssp, sub_svc_parm_ix2, 4) 
                   INDEX(ssp sub_svc_parm_ix2) */ 
      COUNT(SUB_SVC_ID)
 INTO l_total_recs
 FROM SUB_SVC_PARM ssp
WHERE PARM_ID = c_parm_net_ppv_credit_limit
  AND VAL <> '140.00'
  AND EXISTS (SELECT /*+ PARALLEL_INDEX(ss, sub_svc_pk, 4) */ 
                     1
                FROM SUB_SVC ss
               WHERE ss.SUB_SVC_ID = ssp.SUB_SVC_ID
                 AND ss.SUB_SVC_STATUS_ID NOT IN (c_deleted,
                                                  c_inactive,
                                                  c_add_in_progress,
                                                  c_activation_in_progress,
                                                  c_courtesy_block_in_progress,
                                                  c_mso_block_in_progress,
                                                  c_delete_in_progress,
                                                  c_deactivation_in_progress,
                                                  c_change_in_progress));

   DBMS_OUTPUT.PUT_LINE('Total records for updating are : ' || l_total_recs);

   OPEN curPPV_CL;

   LOOP
       FETCH curPPV_CL
        BULK COLLECT INTO rs_sub_svc_id limit ilimit;

       l_rec_cnt := rs_sub_svc_id.COUNT;
       l_curr_cnt := l_curr_cnt + l_rec_cnt;

       DBMS_APPLICATION_INFO.set_module (module_name => 'Procedure: PPV_CREDIT_LIMIT',
                                     action_name => 'Updating ' ||    l_curr_cnt || ' of ' || l_total_recs);

       for idx in 1 .. l_rec_cnt
        LOOP 
          UPDATE SUB_SVC_PARM
         SET VAL = c_ppv_credit_limit
       WHERE SUB_SVC_ID = rs_sub_svc_id(idx)
         AND PARM_ID = c_parm_net_ppv_credit_limit;

      UPDATE SUB_SVC_PARM
         SET VAL = c_ppv_credit_threshold
       WHERE SUB_SVC_ID = rs_sub_svc_id(idx)
         AND PARM_ID = c_parm_ppv_credit_threshold;

    END LOOP;
    COMMIT;


   l_batch := l_batch + 1;
   DBMS_APPLICATION_INFO.set_client_info ('BATCH:' || l_batch * ilimit);
   EXIT WHEN l_rec_cnt < ilimit;

   IF   MOD(l_batch, iCheck) = 0
   THEN
        SELECT STOP_FLAG
          INTO v_stop_flag
          FROM DM_PPV_CREDIT_LIMIT
         WHERE EXECUTION_ID = p_exid;
   END IF;

   EXIT WHEN v_stop_flag = 'Y';

   END LOOP;

   CLOSE curPPV_CL;

DBMS_OUTPUT.PUT_LINE('Have updated records : ' || l_curr_cnt || ' out of total records : ' || l_total_recs  );

EXCEPTION
    WHEN OTHERS THEN
         DBMS_OUTPUT.PUT_LINE('Error: ' || SQLERRM);
END PPV_CREDIT_LIMIT;

创建两个索引后的说明计划-

create index sub_svc_parm_ixpvs on sub_svc_parm (parm_id, val, sub_svc_id) ;

create index sub_svc_ixss on sub_svc (sub_svc_id, sub_svc_status_id) TABLESPACE "SMP_IDX_SUB_SVC" ;


Plan hash value: 176576580

----------------------------------------------------------------------------       --------------------
| Id  | Operation         | Name               | Rows  | Bytes |TempSpc| Cost (%CPU)| Time     |
----------------------------------------------------------------------------    --------------------
|   0 | SELECT STATEMENT  |                    |  1802K|    56M|       | 43296   (1)| 59:49:34 |
|*  1 |  HASH JOIN        |                    |  1802K|    56M|    37M| 43296   (1)| 59:49:34 |
|*  2 |   INDEX FULL SCAN | SUB_SVC_IXSS       |  1763K|    16M|       | 33679   (1)| 46:32:15 |
|*  3 |   INDEX RANGE SCAN| SUB_SVC_PARM_IXPVS |  2308K|    50M|       |  1857   (0)| 02:34:00 |
------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - access("SS"."SUB_SVC_ID"="SSP"."SUB_SVC_ID")
   2 - filter("SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','deleted') AND 
          "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','inactive') AND 
          "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','add_in_progress') AND 
          "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','activation_in_progress') AND 
          "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','courtesy_block_in_progress') 
          AND "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','mso_block_in_progress') 
          AND "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','delete_in_progress') AND 
          "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','deactivation_in_progress') 
          AND "SS"."SUB_SVC_STATUS_ID"<>"FN_GET_STATUS_ID"('SubSvcSpec','change_in_progress'))
   3 - access("SSP"."PARM_ID"="GET_PARM_ID"('net_ppv_credit_limit',"GET_CLASS_ID"('SubSv
          cSpec'),"GET_SVCID"('smp_cpe_cas')))
   filter("SSP"."VAL"<>'140.00')

这有一段评论的时间。

您的查询计划和索引看起来很合理。 这导致人们怀疑函数调用。

您的数据看起来并不大。 几百万行的表。 我怀疑这些被称为无数次。 来自更传统的编程语言的人经常认为,函数是使用SQL进行编程的一种好方法。 不幸的是,它们会产生很多开销,因为它们会阻止优化器执行其工作。

因此,您可以将函数声明为DETERMINISTIC吗? 这将帮助优化器编译它们。 其次,将值存储在CTE中的NOT IN列表中,或者更好的是,将其存储在具有索引的临时表中。 PARM_ID值执行类似的PARM_ID

由于它只是要返回的SUB_SVC_ID ,因此可以从SUB_SVC匹配的SUB_SVC_PARM或SUB_SVC_PARM的SUB_SVC或两者的结合处或交叉处获得。 在任何情况下,优化器都应达到相同的执行计划,但是您仍然可以尝试:

您的查询(使用IN代替EXISTS,它不会改变任何内容,但会增强可读性):

select sub_svc_id
from sub_svc_parm
where parm_id = get_parm_id(...)
and val <> '140.00'
and sub_svc_id in (select sub_svc_id from sub_svc where sub_svc_status_id not in (...));

备选方案1:

select sub_svc_id
from sub_svc
where sub_svc_status_id not in (...)
and sub_svc_id in 
  (select * from sub_svc_parm where parm_id = get_parm_id(...) and val <> '140.00');

备选方案2:

select sub_svc_id
from sub_svc ss
join sub_svc_parm ssp using (sub_svc_id)
where ss.sub_svc_status_id not in (...)
and ssp.parm_id = get_parm_id(...) 
and ssp.val <> '140.00');

备选方案3:

select sub_svc_id
from sub_svc_parm
where parm_id = get_parm_id(...)
and val <> '140.00'
intersect
select sub_svc_id 
from sub_svc 
where sub_svc_status_id not in (...));

无论如何,索引在理想情况下应为: sub_svc_parm(parm_id, val, sub_svc_id)sub_svc(sub_svc_status_id, sub_svc_id) 添加这些可能已经加快了现有查询的速度。

我会先尝试一下而没有任何提示,以查看优化程序为这些查询生成的执行计划。 然后,您可能要再次添加并行化提示。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM