程序花费太多时间来更新数据库

时间:2011-03-31 11:13:11

标签: oracle procedure

CREATE OR REPLACE PROCEDURE UPDATE_CRDT_JV IS
 BEGIN
   UPDATE GL_DISTRIBUTION
         SET GL_DATE = (SELECT ADJ_DATE FROM ADJUSTMENTS WHERE
         ADJ_NUMBER = TO_NUMBER(TR_NUMBER))
   WHERE TR_TYPE = 'ADJST';
   UPDATE GL_DISTRIBUTION 
         SET GL_DATE = (SELECT PARTY_ADJ_DATE FROM PARTY_ADJUSTMENT
         WHERE PARTY_ADJ_NUMBER = TO_NUMBER(TR_NUMBER))
   WHERE TR_TYPE = 'PRTAJ';
   UPDATE GL_DISTRIBUTION
         SET GL_DATE = (SELECT VEN_PAY_VOU_DATE FROM PAYMENTS_TO_VENDORS WHERE
         VEN_PAY_VOU_NUMBER = TO_NUMBER(TR_NUMBER))
   WHERE TR_TYPE = 'CRPAY';
   UPDATE GL_DISTRIBUTION
         SET GL_DATE = (SELECT CHEQUE_DATE FROM SYS_PAYMENTS_HEADER WHERE
         REF_NUMBER = TO_NUMBER(TR_NUMBER))
   WHERE TR_TYPE = 'SYSPY';
   UPDATE GL_DISTRIBUTION
         SET GL_DATE = (SELECT POSTED_DATE FROM PURCHASE_INVOICE_HEADER WHERE
         POSTED_DATE IS NOT NULL AND PIV_NUMBER = TO_NUMBER(TR_NUMBER))
   WHERE TR_TYPE = 'CRINV';
   UPDATE GL_dISTRIBUTION
     SET GL_dATE = (SELECT DOC_dATE FROM REVERSE_HISTORY
           WHERE TR_NUMBER = TO_NUMBER(GL_DISTRIBUTION.TR_NUMBER)
         AND DOC_dATE IS NOT NULL AND TR_TYPE IN ('SYSPY','CRPAY'))
         WHERE TR_TYPE IN ('RSYSPY','RCRPAY');
   commit;
   UPDATE_INV_DET;
 END;

需要超过15分钟才能更新数据库。 现在我通过在SQL PLUS中使用以下查询来更新它:

EXECUTE UPDATE_CRDT_JV;
如果有任何人知道这个问题的解决方案,请帮助我

3 个答案:

答案 0 :(得分:4)

我同意已经给出的建议,以开始计算代码花费时间的位置。但是,你的情况非常普遍,我认为我认识到这种情况:你已经编写了更新语句,以便为该类型的GL_DISTRIBUTION的每一行访问其他表。

解决方案是重写更新语句,我认为有效地实现这两种可能性:

1)更新一个select语句(UPDATE(SELECT ...)SET ... WHERE ...)。这需要一些唯一的键约束或使用BYPASS_UJVC提示。

2)使用MERGE语句。

下面您将看到如何使用单个合并语句重写代码的示例。我期待大的性能提升,因为现在使用每个表的单个外部联接,而不是GL_DISTRIBUTION表中的每一行,对其他表的访问进行一次。

这个例子。测试数据:

SQL> create table gl_distribution (tr_number, tr_type, gl_date)
  2  as
  3  select '1', 'ADJST', date '2011-01-01' from dual union all
  4  select '2', 'ADJST', null from dual union all
  5  select '3', 'PRTAJ', date '2011-01-01' from dual union all
  6  select '4', 'SYSPY', date '2011-01-01' from dual union all
  7  select '5', 'RCRPAY', date '2011-01-01' from dual
  8  /

Table created.

SQL> create table adjustments (adj_number, adj_date)
  2  as
  3  select 1, sysdate from dual union all
  4  select 2, sysdate from dual
  5  /

Table created.

SQL> create table party_adjustment (party_adj_number, party_adj_date)
  2  as
  3  select 3, sysdate from dual union all
  4  select 33, sysdate from dual
  5  /

Table created.

SQL> create table payments_to_vendors (ven_pay_vou_number, ven_pay_vou_date)
  2  as
  3  select 34, sysdate from dual
  4  /

Table created.

SQL> create table sys_payments_header (ref_number,cheque_date)
  2  as
  3  select 4, sysdate from dual
  4  /

Table created.

SQL> create table purchase_invoice_header (piv_number,posted_date)
  2  as
  3  select 35, sysdate from dual
  4  /

Table created.

SQL> create table reverse_history (tr_number,doc_date,tr_type)
  2  as
  3  select 5, sysdate, 'CRPAY' from dual
  4  /

Table created.
SQL>

您的程序(用于比较):

SQL> CREATE OR REPLACE PROCEDURE UPDATE_CRDT_JV
  2  IS
  3  BEGIN
  4    UPDATE GL_DISTRIBUTION
  5    SET GL_DATE = (SELECT ADJ_DATE FROM ADJUSTMENTS WHERE  ADJ_NUMBER = TO_NUMBER(TR_NUMBER))
  6    WHERE TR_TYPE = 'ADJST'
  7    ;
  8    UPDATE GL_DISTRIBUTION
  9    SET GL_DATE = (SELECT PARTY_ADJ_DATE FROM PARTY_ADJUSTMENT
 10    WHERE PARTY_ADJ_NUMBER = TO_NUMBER(TR_NUMBER))
 11    WHERE TR_TYPE = 'PRTAJ'
 12    ;
 13    UPDATE GL_DISTRIBUTION
 14    SET GL_DATE = (SELECT VEN_PAY_VOU_DATE FROM PAYMENTS_TO_VENDORS
 15    WHERE VEN_PAY_VOU_NUMBER = TO_NUMBER(TR_NUMBER))
 16    WHERE TR_TYPE = 'CRPAY'
 17    ;
 18    UPDATE GL_DISTRIBUTION
 19    SET GL_DATE = (SELECT CHEQUE_DATE FROM SYS_PAYMENTS_HEADER WHERE
 20    REF_NUMBER = TO_NUMBER(TR_NUMBER))
 21    WHERE TR_TYPE = 'SYSPY'
 22    ;
 23    UPDATE GL_DISTRIBUTION
 24    SET GL_DATE = (SELECT POSTED_DATE FROM PURCHASE_INVOICE_HEADER WHERE
 25    POSTED_DATE IS NOT NULL AND PIV_NUMBER = TO_NUMBER(TR_NUMBER))
 26    WHERE TR_TYPE = 'CRINV'
 27    ;
 28    UPDATE GL_dISTRIBUTION
 29    SET GL_dATE = (SELECT DOC_dATE FROM REVERSE_HISTORY
 30    WHERE TR_NUMBER = TO_NUMBER(GL_DISTRIBUTION.TR_NUMBER)
 31    AND DOC_dATE IS NOT NULL AND TR_TYPE IN ('SYSPY','CRPAY'))
 32    WHERE TR_TYPE IN ('RSYSPY','RCRPAY')
 33    ;
 34    --commit;
 35    --UPDATE_INV_DET;
 36  END;
 37  /

Procedure created.

SQL>

我的建议:

SQL> create procedure new_update_crdt_jv
  2  as
  3  begin
  4    merge into gl_distribution d
  5    using ( select to_number(d.tr_number) tr_number
  6                 , coalesce
  7                   ( a.adj_date
  8                   , pa.party_adj_date
  9                   , pv.ven_pay_vou_date
 10                   , sph.cheque_date
 11                   , pih.posted_date
 12                   , rh.doc_date
 13                   ) new_date
 14              from gl_distribution d
 15                   left outer join adjustments a
 16                     on to_number(d.tr_number) = a.adj_number
 17                     and d.tr_type = 'ADJST'
 18                   left outer join party_adjustment pa
 19                     on to_number(d.tr_number) = pa.party_adj_number
 20                     and d.tr_type = 'PRTAJ'
 21                   left outer join payments_to_vendors pv
 22                     on to_number(d.tr_number) = pv.ven_pay_vou_number
 23                     and d.tr_type = 'CRPAY'
 24                   left outer join sys_payments_header sph
 25                     on to_number(d.tr_number) = sph.ref_number
 26                     and d.tr_type = 'SYSPY'
 27                   left outer join purchase_invoice_header pih
 28                     on to_number(d.tr_number) = pih.piv_number
 29                     and d.tr_type = 'CRINV'
 30                   left outer join reverse_history rh
 31                     on to_number(d.tr_number) = rh.tr_number
 32                     and rh.tr_type in ('SYSPY','CRPAY')
 33                     and d.tr_type in ('RSYSPY','RCRPAY')
 34          ) n
 35       on ( d.tr_number = n.tr_number)
 36     when matched then
 37          update set d.gl_date = n.new_date
 38    ;
 39  end new_update_crdt_jv;
 40  /

Procedure created.

SQL>

让我们运行你的程序:

SQL> select * from gl_distribution
  2  /

T TR_TYP GL_DATE
- ------ -------------------
1 ADJST  01-01-2011 00:00:00
2 ADJST
3 PRTAJ  01-01-2011 00:00:00
4 SYSPY  01-01-2011 00:00:00
5 RCRPAY 01-01-2011 00:00:00

5 rows selected.

SQL> exec update_crdt_jv

PL/SQL procedure successfully completed.

SQL> select * from gl_distribution
  2  /

T TR_TYP GL_DATE
- ------ -------------------
1 ADJST  31-03-2011 14:41:19
2 ADJST  31-03-2011 14:41:19
3 PRTAJ  31-03-2011 14:41:19
4 SYSPY  31-03-2011 14:41:19
5 RCRPAY 31-03-2011 14:41:19

5 rows selected.

SQL> rollback
  2  /

Rollback complete.

SQL>

我的程序返回相同的结果:

SQL> exec new_update_crdt_jv

PL/SQL procedure successfully completed.

SQL> select * from gl_distribution
  2  /

T TR_TYP GL_DATE
- ------ -------------------
1 ADJST  31-03-2011 14:41:19
2 ADJST  31-03-2011 14:41:19
3 PRTAJ  31-03-2011 14:41:19
4 SYSPY  31-03-2011 14:41:19
5 RCRPAY 31-03-2011 14:41:19

5 rows selected.

希望这有帮助。

的问候,
罗布。

答案 1 :(得分:3)

此问题的解决方案是:

  1. 找出代码的位置 花时间(即简介)
  2. 弄清楚如何加快速度 最慢的部分
  3. 重复直到表演可以接受
  4. 如果您更喜欢猜测,那么您可能想尝试以下任何一项:

    将多个UPDATE组合到单个UPDATE语句中,例如使用@Aklopper所示的CASE条件。

    使用MERGE而不是UPDATE来避免相关子查询。可能会更好,也许不会。

    查看在显示的过程结束时调用的UPDATE_INV_DET过程。

答案 2 :(得分:2)

我来自SQL Server环境,这样的CASE UPDATE语句也不会帮助你(在SQL中使用UPDATE CASE语句的例子(不知道Oracle是否有等效的方法):

   UPDATE titles
           SET GL_DATE=
                     CASE
                       WHEN TR_TYPE = 'ADJST' THEN (SELECT ADJ_DATE FROM ADJUSTMENTS WHERE ADJ_NUMBER = TO_NUMBER(TR_NUMBER) END
                       WHEN TR_TYPE = 'PRTAJ'' THEN (SELECT PARTY_ADJ_DATE FROM PARTY_ADJUSTMENT
         WHERE PARTY_ADJ_NUMBER = TO_NUMBER(TR_NUMBER))
 END
                       ELSE price
                     END