优化存储过程循环以提高性能

时间:2016-09-07 08:07:42

标签: sql oracle

产品表:

PID START_DATE  END_DATE    PRODUCT_SET_ID  CREATED_DATE    UPDATED_DATE    PRODUCT_NAME    COMPANY_NM  PRICE
    1   04/01/11    06/30/11    12              09/06/16        09/06/16        Apple           ABC         50
    2   04/01/10    06/30/10    12              09/06/16        09/06/16        Toothpaste      PQR         80
    3   07/01/11    09/30/11    12              09/06/16        09/06/16        Soap            DOVE        53
    4   04/01/12    06/30/12    12              09/06/16        09/06/16        TV              ONIDA       50000

订单表:

OID PID PRODUCT_ID  SEQ_ID  TYPE_ID CREATED_DATE    UPDATED_DATE    NUMBER_VALUE    TEXT_VALUE
11  1   1           1       1       09/06/16        09/06/16        5
12  1   2           1       6       09/06/16        09/06/16        50  
13  2   1           1       3       09/06/16        09/06/16        3
14  2   2           1       7       09/06/16        09/06/16        80  

VA表:

Product_ID  Product_Name
1           Apple
2           Orange
3           Toothpaste
4           Soap
5           TV
6           ABC
7           PQR
8           DOVE
9           ONIDA

VA_IN_TB

TB_NM       COL_NM     PRODUCT_ID   SEQ_ID
Product_TB  Apple      1           1
Product_TB  Orange     2           1
Product_TB  Toothpaste 3           1
Product_TB  Soap       4           1
Product_TB  TV         5           1
Product_TB  ABC        6           1
Product_TB  PQR        7           1
Product_TB  DOVE       8           1
Product_TB  ONIDA      9           1

我还在产品表中添加了索引:

CREATE INDEX INDX_PID ON PRODUCT_TABLE(PID);

生成订单表的存储过程:

REC_COUNT:= SELECT COUNT(*) FROM PRODUCT_TABLE;
Loop_CT :=( REC_COUNT/2000000) +1;

SELECT MIN(PID) INTO LOWER_LIMIT FROM PRODUCT_TABLE;
UPPER_LIMIT := LOWER_LIMIT +2000000;

FOR i in 1..LOOP_COUNT LOOP
Create Table Temp_1 Nologging as
SELECT  ORDER_SEQ.NEXTVAL OID,
        A.PID,A.PRODUCT_ID,
        A.SEQ_ID,
        A.VALUE,
        B.TYPE_ID 
FROM 
    (
    SELECT  A.PID,
            A.VA_ID,
            A.SEQ_ID,
            A.VALUE,
            B.TYPE_NAME 
    FROM
        (
        select  A.PID, 
                B.PRODUCT_ID,
                B.SEQ_ID, 
                PRODUCT_NAME VALUE 
        from PRODUCT_TABLE A 
        JOIN VA_IN_TB B 
            ON REPLACE(A.PRODUCT_NAME,'''','')=REPLACE(B.COL_NM,'''','') 
            AND A.PID BETWEEN LOWER_LIMIT AND UPPER_LIMIT
        union all
        select  A.PID,
                B.PRODUCT_ID,
                B.SEQ_ID, 
                PRICE VALUE 
        from PRODUCT_TABLE A 
        JOIN VA_IN_TB B 
            ON REPLACE(A.COMPANY_N,'''','')=REPLACE(B.COL_NM,'''','')
            AND A.PID BETWEEN LOWER_LIMIT AND UPPER_LIMIT
        ) A 
    LEFT JOIN VA_TB B 
        ON A.PRODUCT_ID=B.PRODUCT_ID
    ) A 
LEFT JOIN VA_TB B 
    ON A.TYPE_NAME=B.PRODUCT_NAME

Insert INTO ORDER_TB
SELECT * FROM TEMP_1;
Commit;

DROP TABLE TEMP_1;
LOWER_LIMIT := UPPER_LIMIT + 1;
UPPER_LIMIT := UPPER_LIMIT + 2000000;


End LOOP;

我们在产品表中有2000万条记录,我需要使用上述存储过程基于产品表生成订单表。我尽可能地尝试优化,但在Oracle Standard One上执行仍需要8个多小时。我该如何优化此代码?

2 个答案:

答案 0 :(得分:0)

请参阅下文,了解如何使用集合来实现目标。我在开始时询问你的代码是否正常工作因为我看到很多错误,无论如何它都不会运行。我没有测试代码,因为我可以看到你只发布了一部分代码,而且很多东西都不太清楚。我的代码只是提供一个你可以实现的代码片段。

 Declare

    REC_COUNT number;
    Loop_CT   number;
    LOWER_LIMIT number;
    UPPER_LIMIT number;

      Cursor cur_rec (LWR_LMT number,UPR_LMT number)  is 
       /******Assuming all your joins are working fine ..However it doesnot look so from the code your posted. ***/     
       SELECT  ORDER_SEQ.NEXTVAL OID,
                     A.PID,
                     A.PRODUCT_ID,
                     A.SEQ_ID,
                     A.VALUE,
                     B.TYPE_ID 
             FROM 
                 (
                  SELECT  A.PID,
                        A.VA_ID,
                        A.SEQ_ID,
                        A.VALUE,
                        B.TYPE_NAME 
                FROM
                    (
                    select  A.PID, 
                            B.PRODUCT_ID,
                            B.SEQ_ID, 
                            PRODUCT_NAME VALUE 
                    from PRODUCT_TABLE A 
                    JOIN VA_IN_TB B 
                        ON REPLACE(A.PRODUCT_NAME,'''','')= REPLACE(B.PRODUCT_NAME,'''','')                     
                        AND A.PID BETWEEN LWR_LMT AND UPR_LMT
                    union all
                    select  A.PID,
                            B.PRODUCT_ID,
                            B.SEQ_ID, 
                            PRICE VALUE 
                    from PRODUCT_TABLE A 
                    JOIN VA_IN_TB B 
                        ON REPLACE(A.COMPANY_NM,'''','')=REPLACE(B.PRODUCT_NAME,'''','') ----Seems Not correct.
                        AND A.PID BETWEEN LWR_LMT AND UPR_LMT
                    ) A
                LEFT JOIN VA_TB B 
                    ON A.PRODUCT_ID = B.PRODUCT_ID
                ) A
              LEFT JOIN VA_TB B 
                ON A.TYPE_NAME = B.PRODUCT_NAME ;  ----Seems Not correct.

    type var is table of cur_rec%rowtype;

    var_order_tab var;


    begin

    /**Procedure will fail if you try to do anything like this**/
    ---REC_COUNT:= SELECT COUNT(*) FROM PRODUCT_TABLE;

     SELECT COUNT(*)
      into  REC_COUNT
      FROM PRODUCT_TABLE;


     Loop_CT :=( REC_COUNT/2000000)+1;

     SELECT MIN(PID) 
     INTO LOWER_LIMIT 
     FROM PRODUCT_TABLE;

     UPPER_LIMIT := LOWER_LIMIT +2000000;


     Open cur_rec(LOWER_LIMIT,UPPER_LIMIT);
     loop

     fetch cur_rec bulk collect into var_order_tab limit 100;

     FORALL i IN 1 .. var_order_tab.count
           INSERT INTO ORDER_TB
          VALUES var_order_tab(i);

     commit;

     end loop;

   close cur_rec ;

     /**** You cannot create any table like this in any procedure. This is not allowed in PLSQL.
     FOR i in 1..LOOP_COUNT LOOP
     Create Table Temp_1 Nologging as
     SELECT  ORDER_SEQ.NEXTVAL OID,
            A.PID,A.PRODUCT_ID,
            A.SEQ_ID,
            A.VALUE,
            B.TYPE_ID 
    FROM 
        (
        SELECT  A.PID,
                A.VA_ID,
                A.SEQ_ID,
                A.VALUE,
                B.TYPE_NAME 
        FROM
            (
            select  A.PID, 
                    B.PRODUCT_ID,
                    B.SEQ_ID, 
                    PRODUCT_NAME VALUE 
            from PRODUCT_TABLE A 
            JOIN VA_IN_TB B 
                ON REPLACE(A.PRODUCT_NAME,'''','')=REPLACE(B.COL_NM,'''','') 
                AND A.PID BETWEEN LOWER_LIMIT AND UPPER_LIMIT
            union all
            select  A.PID,
                    B.PRODUCT_ID,
                    B.SEQ_ID, 
                    PRICE VALUE 
            from PRODUCT_TABLE A 
            JOIN VA_IN_TB B 
                ON REPLACE(A.COMPANY_N,'''','')=REPLACE(B.COL_NM,'''','')
                AND A.PID BETWEEN LOWER_LIMIT AND UPPER_LIMIT
            ) A 
        LEFT JOIN VA_TB B 
            ON A.PRODUCT_ID=B.PRODUCT_ID
        ) A 
    LEFT JOIN VA_TB B 
        ON A.TYPE_NAME=B.PRODUCT_NAME ****/

    --Insert INTO ORDER_TB
    --SELECT * FROM TEMP_1;
    --Commit;

    /****Neither executing drop command directly is permitted***/
    --DROP TABLE TEMP_1;
    LOWER_LIMIT := UPPER_LIMIT + 1;
    UPPER_LIMIT := UPPER_LIMIT + 2000000;


    --End LOOP;


    end;

答案 1 :(得分:0)

您可以将数据选择到临时表中,然后将临时表的内容复制到表中。为什么不直接在表格中选择数据?

Insert INTO ORDER_TB
select order_seq.nextval oid
      ,a.pid
      ,a.product_id
      ,a.seq_id
      ,a.value
      ,b.type_id
  from (select a.pid
              ,a.va_id
              ,a.seq_id
              ,a.value
              ,b.type_name
          from (select a.pid
                      ,b.product_id
                      ,b.seq_id
                      ,product_name value
                  from product_table a
                  join va_in_tb b
                    on replace(a.product_name, '''', '') = replace(b.col_nm, '''', '')
                   and a.pid between lower_limit and upper_limit
                union all
                select a.pid
                      ,b.product_id
                      ,b.seq_id
                      ,price value
                  from product_table a
                  join va_in_tb b
                    on replace(a.company_n, '''', '') = replace(b.col_nm, '''', '')
                   and a.pid between lower_limit and upper_limit) a
          left join va_tb b
            on a.product_id = b.product_id) a
  left join va_tb b
    on a.type_name = b.product_name