【问题标题】:(oracle) insert in stored procedure very slow compared to insert run manually(oracle)与手动插入相比,在存储过程中插入非常慢
【发布时间】:2017-01-25 06:51:06
【问题描述】:

我从 Oracle sql 工具运行插入,从各种表中选择大约 100.000 行并将它们插入到不同数据库的另一个表中,这在 3 分钟内结束。 我在 oracle 中的存储过程或 PLSQL 中尝试了相同的查询,我们运行了大约 2 个小时。有关信息,我使用源 Oracle 11g 和目标 Oracle 10g

存储过程(每个存储大约需要 2 小时):

CREATE OR REPLACE PROCEDURE MGS.TRANSFER_DATA(a_date in varchar2) -- yyyymmdd
BEGIN
 BEGIN
 FOR xx IN(
  SELECT STR_CD FROM MGS.STORE ORDER BY STR_CD ASC
 ) LOOP
  CALL_LOG(xx.STR_CD,a_date,sysdate,"START INSERT");
  INSERT INTO STOCK@BBS
  SELECT
   STR_CD, STK.DT, PROD_CD, QTY, ORDER_QTY, ORDER_QTY
  FROM
   MGS.STOCK STK, MGS.SALE SAL
   WHERE STK.STR_CD = xx.STR
   AND STK.STR_CD = SAL.STR_CD(+)
   AND STK.PROD_CD = SAL.PROD_CD(+)
   AND STK.DT = SAL.DT(+)
   AND STK.DT = a_date;
 CALL_LOG(xx.STR_CD,a_date,sysdate,"INSERT SUCESSFULL");
END LOOP;
END;

结束传输数据;

我尝试查询 1 家商店(只需 3 分钟):

INSERT INTO STOCK@BBS
SELECT
 STR_CD, STK.DT, PROD_CD, QTY, ORDER_QTY, ORDER_QTY
FROM
 MGS.STOCK STK, MGS.SALE SAL
 WHERE STK.STR_CD = 'STORE01'
 AND STK.STR_CD = SAL.STR_CD(+)
 AND STK.PROD_CD = SAL.PROD_CD(+)
 AND STK.DT= SAL.DT(+)
 AND STK.DT= '20120801'; -- yyyymmdd

【问题讨论】:

  • 想必STOCK表上有索引。你能禁用然后重建这些吗?大型插入需要为每条记录更新索引信息,这可能很慢。如果您以后可以启用/创建它们,最好禁用/删除它们。
  • 使用跟踪查看查询计划,我怀疑 PL/SQL 与 SQL 可能完全不同(它使用硬编码的文字做一件事)。我怀疑如果您在 BBS 端创建程序以便它“拉”而不是“推”,它可能会更好地工作,但我无法验证这一点。

标签: database oracle oracle10g oracle11g


【解决方案1】:

我的最佳猜测是,为过程中的查询生成的计划与为独立查询生成的计划有很大不同。在独立版本中,您可以在其中获得常量,从而允许优化器做出一些好的假设。如果没有常量,优化器就没有什么可做的了,因此可能会做出一些不同的决定。试试这个:

获取独立查询的执行计划。

获取以下的执行计划(只是用子查询重构子句替换你的循环):

WITH xx AS (SELECT STR_CD, '20120801' AS A_DATE
              FROM MGS.STORE ORDER BY STR_CD ASC)
  SELECT STR_CD, "DATE", PROD_CD, QTY, ORDER_QTY, ORDER_QTY
    FROM MGS.STOCK STK, MGS.SALE SAL
    WHERE STK.STR_CD = xx.STR AND
          STK.STR_CD = SAL.STR_CD(+) AND
          STK.PROD_CD = SAL.PROD_CD(+) AND
          STK."DATE" = SAL."DATE"(+) AND
          STK."DATE" = xx.A_DATE

(请注意,我必须对名为 DATE 的列进行双引号。我很惊讶您没有遇到这种情况)。比较计划。由于子查询分解子句的存在,第二个计划显然与第一个不同,但尝试比较匹配的元素。特别是查找第一个查询使用索引而第二个查询执行全表扫描的元素。

根据需要向第二个查询添加提示,使其计划尽可能与第一个查询匹配。

我认为不太可能的另一种可能性是日志调用是花费时间的地方。对于笑容,您可以尝试评论注销以查看是否有效果(我没想到,但到目前为止生活充满了我没想到的事情:-)。

分享和享受。

【讨论】:

    【解决方案2】:

    我不知道为什么 plsql 中的查询变慢了,但我有下面的解决方案:

    CREATE OR REPLACE PROCEDURE MGS.TRANSFER_DATA(a_date in varchar2) -- yyyymmdd
    BEGIN
     BEGIN
     FOR xx IN(
      SELECT STR_CD FROM MGS.STORE ORDER BY STR_CD ASC
     ) LOOP
      CALL_LOG(xx.STR_CD,a_date,sysdate,"START INSERT");
      execute immediate '
      INSERT INTO STOCK@BBS
      SELECT
       STR_CD, STK.DT, PROD_CD, QTY, ORDER_QTY, ORDER_QTY
      FROM
       MGS.STOCK STK, MGS.SALE SAL
       WHERE STK.STR_CD = ''' || xx.STR || '''
       AND STK.STR_CD = SAL.STR_CD(+)
       AND STK.PROD_CD = SAL.PROD_CD(+)
       AND STK.DT = SAL.DT(+)
       AND STK.DT = ''' || a_date || '''
     ';
     CALL_LOG(xx.STR_CD,a_date,sysdate,"INSERT SUCESSFULL");
    END LOOP;
    END;
    

    【讨论】:

    • 如果您这样做,请注意 SQL 注入。性能可能会提高,因为您给了 Oracle 实际文字值而不是绑定变量。更多信息使其能够制定更好的计划(以花费更多时间制定计划为代价)。
    【解决方案3】:

    尝试通过 dbms_job 为每个存储并行启动所有这些插入。可能允许实现与传统 sql 相同的性能。我觉得 dbms_parallel_execute 也可以帮到你,请参考http://www.oracle-base.com/articles/11g/dbms_parallel_execute_11gR2.php

    【讨论】:

      猜你喜欢
      • 2012-07-24
      • 1970-01-01
      • 2014-04-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-27
      • 1970-01-01
      相关资源
      最近更新 更多