【问题标题】:performance tuning sql query性能调优sql查询
【发布时间】:2016-06-12 10:00:21
【问题描述】:

我有这个查询,它是性能调整的候选者,因为它需要超过 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)

同时我正在尝试对调用 where 子句的函数使用 WITH 子句,但是在过程中,函数被捕获在常量变量中,然后在查询中使用!

我的数据库是:Oracle Database 11g Enterprise Edition Release 11.2.0.2.0 - 64bit Production

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

编辑:这个过程正在使用查询,不是我写的! 我认为这个过程本身需要调整而不是查询

请推荐

 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')

【问题讨论】:

  • 在游标中使用。 Ca you clairfy:当您在游标中运行您提供的查询 not 时需要那么长时间吗?
  • 我正在更新问题并添加使用此查询的过程
  • 您能否澄清一下:是 procedure 还是 query 需要 4-5 小时?如果是程序,那么查询需要多长时间?
  • 我正在执行该查询并从过去 1.30 小时开始运行 :( 并在列上创建新索引后检查创建索引后更新解释计划的问题
  • 首先,您必须知道这 4-5 个小时到底花在了哪里。 community.oracle.com/thread/503834?tstart=0

标签: sql oracle plsql sqlperformance


【解决方案1】:

评论有点长。

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

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

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

【讨论】:

  • 我也可以用 SQL 查询替换这些函数,如果我在 with 子句中使用这些函数,那么这里会有帮助。是的,因为这些查询不是我写的,所以我总是尽量避免在 SQL 中调用函数。还有一个问题是如何通过查看解释计划来了解表格大小!
  • @mradul 。 . . “行”列表示预期的数据量。
  • 如果我没记错,但这些行是每个操作返回的行!
【解决方案2】:

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

您的查询(使用 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)。添加这些可能已经加速了现有的查询。

我会在没有任何提示的情况下先尝试一下,看看优化器为这些查询生成了哪些执行计划。然后您可能需要再次添加并行化提示。

【讨论】:

  • 我尝试了您提供的所有替代方案,但没有一个有帮助:(所有的成本都相同,但是 intersect 选项会增加成本。我想我需要在 sub-svc_status_id 列上建立索引并删除从 where 子句调用的函数!!
  • 好的,如上所述,优化器应该产生相同的计划,所以我并不惊讶它没有帮助。这只是一个微小的机会,仅此而已。但是您是否添加了两个索引?这些很可能会加快查询速度。
  • 是的,我也在想同样的事情,我需要为此获得许可,因为它是一个现场域,即使我使用简单的 SQL 查询更改了函数以及您的所有替代方案,解释计划也是相同的提供!\
  • 另外我想知道我是否需要为两列添加索引,或者如果我只为 sub_svc_status_id 添加是否可以,因为 sub_svc_id 已经是一个主键
  • 我在 sub_svc_statis_id 列上添加了列,它将成本降低到 505K :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-07
  • 2013-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多