存储过程花费的时间太长,有没有更好的方法来做到这一点/优化?

Posted

技术标签:

【中文标题】存储过程花费的时间太长,有没有更好的方法来做到这一点/优化?【英文标题】:Stored Procedure taking too long, is there a better way to do this / optimize? 【发布时间】:2018-08-29 20:35:49 【问题描述】:

目前,我正在尝试通过数据库链接批量插入大量数据(约 500,000 行)。我正在从物化视图中获取数据。我打算添加索引,但在某处读到这实际上会减慢进程。插入行后,我将获取唯一 ID 并将它们插入到标记表中,以便将它们标记为“已插入”并且不会再次插入。然而,这个过程现在已经停滞了大约 30 分钟。有一个更好的方法吗? (以下是我的代码)。

create or replace PROCEDURE   SEND_DATA
IS
   CURSOR cursora
   IS
      SELECT DISTINCT unique_id_1
        FROM mv_1;

   CURSOR cursorb
   IS 
      SELECT DISTINCT unique_id_2
       FROM mv_2;

ca cursora%ROWTYPE;
cb cursorb%ROWTYPE;

    sent_flag NUMBER(10);
BEGIN
    SELECT flag_id
     INTO sent_flag
     FROM flag f
    WHERE f.flag_tx = 'SENT';
---
Delete FROM TABLE1@db1
      WHERE to_date(to_char(LOCAL_TIMESTAMP,'mm/dd/yyyy'),'mm/dd/yyyy') || code in 
 (SELECT distinct to_date(to_char(LOCAL_TIME_TS,'mm/dd/yyyy'),'mm/dd/yyyy'), code FROM MV_1);
COMMIT;
    Delete FROM TABLE1@db1 
          WHERE type || timestamp in (SELECT DATA_Type_TX || UTC_TS FROM MV_1);
    COMMIT;
    insert into TABLE1@db1(DATE, TYPE, VALUE, LAST_UPDATE, FLAG, LOCAL_TIMESTAMP)
    SELECT DATA_DATE,  NAME, VALUE, SYSDATE, null, LOCAL_TIME
  FROM MV_2 A;

COMMIT;
OPEN cursora;

LOOP
 FETCH cursora into ra;
 EXIT WHEN cursora%NOTFOUND;
 INSERT INTO flag(
    SUBMIT_ID,
    FLAG_ID,
    CREATE_USER_ID,
         CREATE_DT)
   VALUES (
    rdba.SUBMIT_ID,
    SENT_FLAG,
    '1',
         sysdate);
END LOOP;
CLOSE cursora;
COMMIT;
---
EXCEPTION
      WHEN OTHERS
      THEN
           NULL;
       RAISE;
    END SEND_DATA;

【问题讨论】:

不要循环插入。请改用insert ... select 使用循环/游标方法比批量插入有什么好处吗? 可怕的逐行方法总是比较慢(有些人称之为逐行缓慢) 逐行方法光标是否正确?不是批量插入? (对不起这个愚蠢的问题) 您的代码中没有“批量插入”。您需要将一批行提取到集合中,然后将 insert 与 FORALL 语句一起使用,以获得“批量选择/插入”功能。详情见这篇文章:On BULK COLLECT。但在您的情况下,最简单和最快的方法很简单:INSERT ... SELECT ... 语句。例如看这个问题:***.com/questions/7323407/… 【参考方案1】:

你的程序有几个缺陷,实际上它应该会失败。

create or replace PROCEDURE   SEND_DATA IS
   CURSOR cursora IS
      SELECT DISTINCT unique_id_1
        FROM mv_1;

   CURSOR cursorb IS 
      SELECT DISTINCT unique_id_2
       FROM mv_2;

游标cursorb没有用在procedure上,为什么要声明它?

Delete FROM TABLE1@db1
      WHERE to_date(to_char(LOCAL_TIMESTAMP,'mm/dd/yyyy'),'mm/dd/yyyy') || code in 
 (SELECT distinct to_date(to_char(LOCAL_TIME_TS,'mm/dd/yyyy'),'mm/dd/yyyy'), code FROM MV_1);

这应该会失败,因为首先连接两列,但在 IN () 中选择两列。无论如何,删除DISTINCT 子句 - 它没用。

    Delete FROM TABLE1@db1 
          WHERE type || timestamp in (SELECT DATA_Type_TX || UTC_TS FROM MV_1);

您不应使用保留关键字如TIMESTAMP 作为列名。

LOOP
 FETCH cursora into ra;
 EXIT WHEN cursora%NOTFOUND;
 INSERT INTO flag(SUBMIT_ID, FLAG_ID, CREATE_USER_ID, CREATE_DT)
   VALUES ( rdba.SUBMIT_ID, SENT_FLAG, '1', sysdate);
END LOOP;

为什么用引号将数值括起来(即'1')?这段代码也应该失败,因为变量rardba 没有被声明。我猜是

LOOP
 FETCH cursora into ca;
 EXIT WHEN cursora%NOTFOUND;
 INSERT INTO flag(SUBMIT_ID, FLAG_ID, CREATE_USER_ID, CREATE_DT)
   VALUES ( ca.unique_id_1, SENT_FLAG, '1', sysdate);
END LOOP;

改写为

INSERT INTO flag (SUBMIT_ID, FLAG_ID, CREATE_USER_ID, CREATE_DT)
SELECT DISTINCT unique_id_1, SENT_FLAG, 1, sysdate
FROM mv_1;

假设上面的逻辑是正确的

EXCEPTION
      WHEN OTHERS
      THEN
           NULL;
       RAISE;

这部分完全没用。 WHEN OTHERS THEN NULL; 的意思是“忽略任何错误”,但在下一行你提出它。

【讨论】:

谢谢,是的,我不得不修改 proc,使它成为一个更小的“更易读”的例子,并且在这样做的过程中可能犯了一些愚蠢的错误(对不起!)。我在 '1' 周围使用引号的原因是因为这是一个 varchar 字段,但我们想要的 id 是一个数字(我知道很奇怪)。在这种情况下,您认为最佳“例外”是什么?一如既往地感谢您的持续帮助:) 如果您正在寻找相同的异常行为,只需完全跳过它。 处理这个问题的正确方法是什么?我担心的是 1 个条目/行被疯了,结果我的整个过程都失败了。 好吧,第一个操作是:删除所有COMMT; 命令。最后提交一个 COMMIT - 或者由调用者在外部提交。 我注意到有时当我移动大量数据时;这个过程被束缚了。我在某处读到,如果我尽可能多地承诺会有帮助吗? (原谅我的无知)

以上是关于存储过程花费的时间太长,有没有更好的方法来做到这一点/优化?的主要内容,如果未能解决你的问题,请参考以下文章

有很多:通过,更好的方法来做到这一点?

jQuery Hide/Show with Slide on Hover...更好的方法来做到这一点?

存储结果 ThreadPoolExecutor

我怎样才能立即获得这个 itertools.product() 的长度,因为 len(list()) 将花费无限长的时间来做到这一点?

将文件从 s3 复制到 redshift 花费的时间太长

有没有更好的方法来抓取这些数据?