如何优化下面的循环执行700次的代码?

时间:2016-11-04 07:53:28

标签: sql oracle plsql

循环开始如下:

   FOR z in 1 .. val LOOP  --val is some variable

    select add_months(p_in_usageMonth, - (z - 1))
      INTO v_usage_month
      from dual; ---simple select


    select count(*)
      into n_invoice_exists
      from fmo_op2_invoice inv
     where inv.usage_month = v_usage_month
       and contract_seq_id = p_in_contractId
       and inv.invoice_status_id =
           (select status_seq_id
              from fmo_op2_status
             where status_type = 'INVOICE'
               and fmo_op2_status.status_description = 'SUCCESS')
       and invoice_num not in
           (select NVL(rev.cancelled_invoice_number, 'X')
              from fmo_op2_invoice rev)
       and NVL(engine_serial_number, '%') like NVL(p_in_esn, '%')
       and inv.billing_invoice_type not in ('C', 'M', 'R');


    IF n_invoice_exists = 0 THEN

- fmo_op2_contract表中的索引 - --UNIQUE- ONPOINT_CONTRACT_NUMBER,CONTRACT_SEQ_ID,CONTRACT_NAME --NORMAL- FIN_SSO,CONTRACT_SEQ_ID,CONTRACT_SEQ_ID

      IF upper(p_in_billingType) = 'POPULAR' or
         upper(p_in_billingType) = 'RESTORED' THEN
        SELECT UPPER(nvl(usage_type_popular,
                         fmo_op2_contract.usage_type_restored)),
               UPPER(nvl(fmo_op2_contract.usage_category_popular,
                         fmo_op2_contract.usage_category_restored))
          INTO v_usage_type, v_usage_category
          FROM fmo_op2_contract
         WHERE contract_seq_id = p_in_contractId;

        IF v_usage_type = 'ENGINE' THEN

          if p_in_esn is null then
            v_engine_serial_number := '%';
          else
            v_engine_serial_number := p_in_esn;
          end if;

- fmo_op2_invoice的索引 --UNIQUE-INVOICE_NUM

          select count(*)
            into v_tot
            from (select distinct invoice_num
                    from fmo_op2_invoice inv
                   where inv.usage_month = v_usage_month
                     and contract_seq_id = p_in_contractId
                     and inv.invoice_status_id =
                         (select status_seq_id
                            from fmo_op2_status
                           where status_type = 'INVOICE'
                             and fmo_op2_status.status_description =
                                 'SUCCESS')
                     and invoice_num not in
                         (select NVL(rev.cancelled_invoice_number, 'X')
                            from fmo_op2_invoice rev)
                     and NVL(engine_serial_number, '%') like
                         v_engine_serial_number);

          IF v_tot > 0 THEN
            select distinct invoice_num
              INTO v_inv_num
              from fmo_op2_invoice inv
             where inv.usage_month = v_usage_month
               and contract_seq_id = p_in_contractId
               and inv.invoice_status_id =
                   (select status_seq_id
                      from fmo_op2_status
                     where status_type = 'INVOICE'
                       and fmo_op2_status.status_description = 'SUCCESS')
               and invoice_num not in
                   (select NVL(rev.cancelled_invoice_number, 'X')
                      from fmo_op2_invoice rev)
               and NVL(engine_serial_number, '%') like
                   v_engine_serial_number;

            SELECT COUNT(*)
              INTO v_inv_exists
              FROM (SELECT *
                      from fmo_op2_invoice
                     WHERE invoice_num = v_inv_num);



            IF v_inv_exists > 0 THEN
              --if inv present

              SELECT COUNT(*)
                INTO v_cancelled
                FROM (SELECT *
                        FROM fmo_op2_invoice a, fmo_op2_status b
                       WHERE a.cancelled_invoice_number = v_inv_num
                         AND a.invoice_status_id = b.status_seq_id
                         AND b.status_code = 'S');

              IF v_cancelled = 0 THEN
                RAISE ex_custom;
              END IF;
            END IF;
          END IF;

          DELETE FROM fmo_op2_engine_usage_factors
           WHERE usage_month = v_usage_month
             and contract_seq_id = p_in_contractId;

          DELETE FROM fmo_op2_engine_usage
           WHERE contract_seq_id = p_in_contractId
             and usage_month = v_usage_month
         AND upper(usage_category)<>'BASL';

---在循环中调用另一个过程。               save_engine_utilization_prc(p_in_contractId,                                           p_in_billingType,                                           v_usage_month,                                           p_in_esn,                                           p_in_util_data_list,                                           p_in_fact_avg_list,                                           p_in_login_id,                                           VAL,                                           Z,                                           p_out_success_status,                                           p_out_esn_check_fails,                                           p_out_error);

          SELECT v_i - 1 INTO p_out_count FROM DUAL;

        ELSIF v_usage_type = 'FLEET' THEN

          select count(*)
            into v_tot
            from (select distinct invoice_num
                    FROM fmo_op2_fleet_usage
                   WHERE trim(subfleet_id) in
                         (select subfleet_id
                            from fmo_op2_fleet e
                           where e.contract_seq_id = p_in_contractId
                             and e.valid_ind = 'Y')
                     and usage_month = v_usage_month
                     and invoice_num is not null
                     and contract_seq_id = p_in_contractId);

          IF v_tot > 0 THEN
            -- tot invoice
            select distinct invoice_num
              INTO v_inv_num
              FROM fmo_op2_fleet_usage
             WHERE trim(subfleet_id) in
                   (select subfleet_id
                      from fmo_op2_fleet e
                     where e.contract_seq_id = p_in_contractId
                       and e.valid_ind = 'Y'

                    )
               and usage_month = v_usage_month
               and invoice_num is not null
               and contract_seq_id = p_in_contractId;

            SELECT COUNT(*)
              INTO v_inv_exists
              FROM (SELECT *
                      from fmo_op2_invoice
                     WHERE invoice_num = v_inv_num);

            IF v_inv_exists > 0 THEN


              SELECT COUNT(*)
                INTO v_cancelled
                FROM (SELECT *
                        FROM fmo_op2_invoice a, fmo_op2_status b
                       WHERE a.cancelled_invoice_number = v_inv_num
                         AND a.invoice_status_id = b.status_seq_id
                         AND b.status_code = 'S');

              IF v_cancelled = 0 THEN
                RAISE ex_custom;
              END IF;
            END IF;
          END IF;

          DELETE FROM fmo_op2_fleet_usage_factors
           WHERE contract_seq_id = p_in_contractId
             and usage_month = v_usage_month;

          DELETE FROM fmo_op2_fleet_usage
           WHERE contract_seq_id = p_in_contractId
             and usage_month = v_usage_month
             and invoice_num is null; 
        ---another procedure call
          save_fleet_utilization_prc(p_in_contractId,
                                     p_in_billingType,
                                     v_usage_month,
                                     p_in_esn,
                                     p_in_util_data_list,
                                     p_in_fact_avg_list,
                                     p_in_login_id,
                                     val,
                                     z,
                                     p_out_success_status,
                                     p_out_esn_not_exists,
                                     p_out_esn_check_fails,
                                     p_out_error);

        ELSIF v_usage_type = 'TAIL' THEN

          select count(*)
            into v_tot
            from (select distinct invoice_num
                    FROM fmo_op2_tail_usage
                   WHERE trim(tail_number) in
                         (select tail_number
                            from fmo_op2_engine_master e
                           where e.contract_seq_id = p_in_contractId
                             and e.valid_ind = 'Y')
                     and usage_month = v_usage_month
                     and invoice_num is not null);

          IF v_tot > 0 THEN
            -- tot invoice
            select distinct invoice_num
              INTO v_inv_num
              FROM fmo_op2_tail_usage
             WHERE trim(subfleet_id) in
                   (select subfleet_id
                      from fmo_op2_fleet e
                     where e.contract_seq_id = p_in_contractId
                       and e.valid_ind = 'Y')
               and usage_month = v_usage_month
               and invoice_num is not null;

            SELECT COUNT(*)
              INTO v_inv_exists
              FROM (SELECT *
                      from fmo_op2_invoice
                     WHERE invoice_num = v_inv_num);

            IF v_inv_exists > 0 THEN
              --if inv present

- fmo_op2_status上的索引 --STATUS_SEQ_ID -UNIQUE

              SELECT COUNT(*)
                INTO v_cancelled
                FROM (SELECT *
                        FROM fmo_op2_invoice a, fmo_op2_status b
                       WHERE a.cancelled_invoice_number = v_inv_num
                         AND a.invoice_status_id = b.status_seq_id
                         AND b.status_code = 'S');

              IF v_cancelled = 0 THEN
                RAISE ex_custom;
              END IF;
            END IF;
          END IF;

          DELETE FROM fmo_op2_tail_usage_factors
           WHERE contract_seq_id = p_in_contractId
             and usage_month = v_usage_month;

          DELETE FROM fmo_op2_tail_usage
           WHERE contract_seq_id = p_in_contractId
             and usage_month = v_usage_month
             and invoice_num is null; 


          save_tail_utilization_prc(p_in_contractId,
                                    p_in_billingType,
                                    v_usage_month,
                                    p_in_esn,
                                    p_in_util_data_list,
                                    p_in_fact_avg_list,
                                    p_in_login_id,
                                    val,
                                    z,
                                    p_out_success_status,
                                    p_out_esn_not_exists,
                                    p_out_esn_check_fails,
                                    p_out_error);

          SELECT v_i - 1 INTO p_out_count FROM DUAL;
          SELECT v_j - 1 INTO p_out_check_fail_count FROM DUAL;



        END IF;
      END IF;
    END IF;

  END LOOP;

- 每张表的记录数约为1500。 此循环执行700次并占用大量时间。 我该如何优化呢?

1 个答案:

答案 0 :(得分:0)

有一些问题,我将它们标记为内联: 首先要注意的是,你正在使用第二个查询的核心很多,也许你应该考虑对一个更大的&#34;进行相同的查询。  结果集,但你会&#34;计算&#34;它一次。或者更快,请参阅内联评论

FOR z in 1 .. val LOOP  --val is some variable


select add_months(p_in_usageMonth, - (z - 1))
  INTO v_usage_month
  from dual; ---simple select


select count(*)
  into n_invoice_exists
  from fmo_op2_invoice inv
 where inv.usage_month = v_usage_month
   and contract_seq_id = p_in_contractId
   and inv.invoice_status_id =               
       --issue No.1: remove that query from here and put it into a WITH or a subselect for a join
       (select status_seq_id
          from fmo_op2_status
         where status_type = 'INVOICE'
           and fmo_op2_status.status_description = 'SUCCESS')
       --issue No.2.: remove it too like above, using a left outer and an IS NULL condition in the where
   and invoice_num not in
       (select NVL(rev.cancelled_invoice_number, 'X')
          from fmo_op2_invoice rev)
       --issue No.3.: replace like to = , it's pointless to search with like and more expensive 
   and NVL(engine_serial_number, '%') like NVL(p_in_esn, '%')
   and inv.billing_invoice_type not in ('C', 'M', 'R');


IF n_invoice_exists = 0 THEN

  IF upper(p_in_billingType) = 'POPULAR' or
     upper(p_in_billingType) = 'RESTORED' THEN
    SELECT UPPER(nvl(usage_type_popular,
                     fmo_op2_contract.usage_type_restored)),
           UPPER(nvl(fmo_op2_contract.usage_category_popular,
                     fmo_op2_contract.usage_category_restored))
      INTO v_usage_type, v_usage_category
      FROM fmo_op2_contract
     WHERE contract_seq_id = p_in_contractId;

    IF v_usage_type = 'ENGINE' THEN

      if p_in_esn is null then
        v_engine_serial_number := '%';
      else
        v_engine_serial_number := p_in_esn;
      end if;

      select count(*)
        into v_tot
        from (
              --issue No.4.: replace distinct to "group by", 'cause group by using hash instead of expensive sorting of the distinct. And the rest issues are  the same like above
              select distinct invoice_num
                from fmo_op2_invoice inv
               where inv.usage_month = v_usage_month
                 and contract_seq_id = p_in_contractId
                 and inv.invoice_status_id =
                     (select status_seq_id
                        from fmo_op2_status
                       where status_type = 'INVOICE'
                         and fmo_op2_status.status_description =
                             'SUCCESS')
                 and invoice_num not in
                     (select NVL(rev.cancelled_invoice_number, 'X')
                        from fmo_op2_invoice rev)
                 and NVL(engine_serial_number, '%') like
                     v_engine_serial_number);

      IF v_tot > 0 THEN
       --issue No.5.: see above
        select distinct invoice_num
          INTO v_inv_num
          from fmo_op2_invoice inv
         where inv.usage_month = v_usage_month
           and contract_seq_id = p_in_contractId
           and inv.invoice_status_id =
               (select status_seq_id
                  from fmo_op2_status
                 where status_type = 'INVOICE'
                   and fmo_op2_status.status_description = 'SUCCESS')
           and invoice_num not in
               (select NVL(rev.cancelled_invoice_number, 'X')
                  from fmo_op2_invoice rev)
           and NVL(engine_serial_number, '%') like
               v_engine_serial_number;

        SELECT COUNT(*)
          INTO v_inv_exists
          FROM (SELECT *
                  from fmo_op2_invoice
                 WHERE invoice_num = v_inv_num);


        IF v_inv_exists > 0 THEN
          --if inv present

          SELECT COUNT(*)
            INTO v_cancelled
            FROM (SELECT *
                    FROM fmo_op2_invoice a, fmo_op2_status b
                   WHERE a.cancelled_invoice_number = v_inv_num
                     AND a.invoice_status_id = b.status_seq_id
                     AND b.status_code = 'S');

          IF v_cancelled = 0 THEN
            RAISE ex_custom;
          END IF;
        END IF;
      END IF;

      DELETE FROM fmo_op2_engine_usage_factors
       WHERE usage_month = v_usage_month
         and contract_seq_id = p_in_contractId;

      DELETE FROM fmo_op2_engine_usage
       WHERE contract_seq_id = p_in_contractId
         and usage_month = v_usage_month
     AND upper(usage_category)<>'BASL';


save_engine_utilization_prc(p_in_contractId, p_in_billingType, v_usage_month, p_in_esn, p_in_util_data_list, p_in_fact_avg_list, p_in_login_id, val, z, p_out_success_status, p_out_esn_check_fails, p_out_error);

      --issue No.6.: avoiding unnecessary context switching, replace that stmt to a simple  p_out_count := v_i -1 ;
      SELECT v_i - 1 INTO p_out_count FROM DUAL;

    ELSIF v_usage_type = 'FLEET' THEN
      --issue No.7.: just like above
      select count(*)
        into v_tot
        from (select distinct invoice_num
                FROM fmo_op2_fleet_usage
               WHERE trim(subfleet_id) in
                     (select subfleet_id
                        from fmo_op2_fleet e
                       where e.contract_seq_id = p_in_contractId
                         and e.valid_ind = 'Y')
                 and usage_month = v_usage_month
                 and invoice_num is not null
                 and contract_seq_id = p_in_contractId);

      IF v_tot > 0 THEN
        -- tot invoice
        --issue No.8.: same
        select distinct invoice_num
          INTO v_inv_num
          FROM fmo_op2_fleet_usage
         WHERE trim(subfleet_id) in
               (select subfleet_id
                  from fmo_op2_fleet e
                 where e.contract_seq_id = p_in_contractId
                   and e.valid_ind = 'Y'

                )
           and usage_month = v_usage_month
           and invoice_num is not null
           and contract_seq_id = p_in_contractId;

        SELECT COUNT(*)
          INTO v_inv_exists
          FROM (SELECT *
                  from fmo_op2_invoice
                 WHERE invoice_num = v_inv_num);

        IF v_inv_exists > 0 THEN


          SELECT COUNT(*)
            INTO v_cancelled
            FROM (SELECT *
                    FROM fmo_op2_invoice a, fmo_op2_status b
                   WHERE a.cancelled_invoice_number = v_inv_num
                     AND a.invoice_status_id = b.status_seq_id
                     AND b.status_code = 'S');

          IF v_cancelled = 0 THEN
            RAISE ex_custom;
          END IF;
        END IF;
      END IF;

      DELETE FROM fmo_op2_fleet_usage_factors
       WHERE contract_seq_id = p_in_contractId
         and usage_month = v_usage_month;

      DELETE FROM fmo_op2_fleet_usage
       WHERE contract_seq_id = p_in_contractId
         and usage_month = v_usage_month
         and invoice_num is null; 

      save_fleet_utilization_prc(p_in_contractId,
                                 p_in_billingType,
                                 v_usage_month,
                                 p_in_esn,
                                 p_in_util_data_list,
                                 p_in_fact_avg_list,
                                 p_in_login_id,
                                 val,
                                 z,
                                 p_out_success_status,
                                 p_out_esn_not_exists,
                                 p_out_esn_check_fails,
                                 p_out_error);

    ELSIF v_usage_type = 'TAIL' THEN

      --issue No.9.: same
      select count(*)
        into v_tot
        from (select distinct invoice_num
                FROM fmo_op2_tail_usage
               WHERE trim(tail_number) in
                     (select tail_number
                        from fmo_op2_engine_master e
                       where e.contract_seq_id = p_in_contractId
                         and e.valid_ind = 'Y')
                 and usage_month = v_usage_month
                 and invoice_num is not null);

      IF v_tot > 0 THEN
        -- tot invoice
        --issue No.10.: same
        select distinct invoice_num
          INTO v_inv_num
          FROM fmo_op2_tail_usage
         WHERE trim(subfleet_id) in
               (select subfleet_id
                  from fmo_op2_fleet e
                 where e.contract_seq_id = p_in_contractId
                   and e.valid_ind = 'Y')
           and usage_month = v_usage_month
           and invoice_num is not null;

        SELECT COUNT(*)
          INTO v_inv_exists
          FROM (SELECT *
                  from fmo_op2_invoice
                 WHERE invoice_num = v_inv_num);

        IF v_inv_exists > 0 THEN
          --if inv present


          SELECT COUNT(*)
            INTO v_cancelled
            FROM (SELECT *
                    FROM fmo_op2_invoice a, fmo_op2_status b
                   WHERE a.cancelled_invoice_number = v_inv_num
                     AND a.invoice_status_id = b.status_seq_id
                     AND b.status_code = 'S');

          IF v_cancelled = 0 THEN
            RAISE ex_custom;
          END IF;
        END IF;
      END IF;

      DELETE FROM fmo_op2_tail_usage_factors
       WHERE contract_seq_id = p_in_contractId
         and usage_month = v_usage_month;

      DELETE FROM fmo_op2_tail_usage
       WHERE contract_seq_id = p_in_contractId
         and usage_month = v_usage_month
         and invoice_num is null; 


      save_tail_utilization_prc(p_in_contractId,
                                p_in_billingType,
                                v_usage_month,
                                p_in_esn,
                                p_in_util_data_list,
                                p_in_fact_avg_list,
                                p_in_login_id,
                                val,
                                z,
                                p_out_success_status,
                                p_out_esn_not_exists,
                                p_out_esn_check_fails,
                                p_out_error);
      ----issue No.11.: avoid contextswitching 
      SELECT v_i - 1 INTO p_out_count FROM DUAL;
      SELECT v_j - 1 INTO p_out_check_fail_count FROM DUAL;



    END IF;
  END IF;
END IF;

END LOOP;

并持续到最后,我会检查DELETE stms的exec.time。