oracle批量更新之使用遊標進行分批次更新的5種方式及速度比對

1.情景展現

  一共有22w條數據, 須要將A表的主鍵更新至B表的指定字段,如何快速完成更新?html

2.解決方案

  聲明:sql

  解決方案不僅一種,該文章只介紹快速遊標法及代碼實現;數據庫

  兩張表的ID和ID_CARD字段都創建了索引。 數組

  方式一:使用隱式遊標(更新一次提交1次)oracle

--快速遊標法
BEGIN
  FOR TEMP_CURSOR IN (SELECT T2.ID, T2.ID_CARD
                        FROM VIRTUAL_CARD10 T1, PRIMARY_INDEX10 T2
                       WHERE T1.ID_CARD = T2.ID_CARD
                         AND T1.REMARK = '**市****區數據'
                         AND T2.REMARK = '**市****區數據') LOOP
    /* LOOP循環的是TEMP_CURSOR(逐條讀取TEMP_CURSOR) */
    UPDATE VIRTUAL_CARD10
       SET INDEX_ID = TEMP_CURSOR.ID
     WHERE ID_CARD = TEMP_CURSOR.ID_CARD;
    COMMIT; --提交
  END LOOP;
END;

  執行時間:性能

  方式二:使用隱式遊標(更新1000次提交1次)(推薦使用)fetch

/* 使用隱式遊標進行分批次更新 */
DECLARE
  V_COUNT NUMBER(10);
BEGIN
  /* 隱式遊標 */
  FOR TEMP_CURSOR IN (SELECT T2.ID, T2.ID_CARD
                        FROM VIRTUAL_CARD10 T1, PRIMARY_INDEX10 T2
                       WHERE T1.ID_CARD = T2.ID_CARD
                         AND T1.REMARK = '**市****區數據'
                         AND T2.REMARK = '**市****區數據') LOOP
    /* 業務邏輯 */
    UPDATE VIRTUAL_CARD10
       SET INDEX_ID = TEMP_CURSOR.ID
     WHERE ID_CARD = TEMP_CURSOR.ID_CARD;
    /* 更新一次,+1 */
    V_COUNT := V_COUNT + 1;
    /* 1000條提交1次 */
    IF V_COUNT >= 1000 THEN
      COMMIT; --提交
      V_COUNT := 0; --重置
    END IF;
  END LOOP;
  COMMIT; -- 提交全部數據,把這個去掉,能夠查看是不是本身想要的效果,再決定是否提交
END;

  執行時間:spa

  方式三:顯式遊標+分批次更新(1000條1提交)htm

/* 使用遊標進行分批次更新 */
DECLARE
  V_COUNT    NUMBER(10);
  V_INDEX_ID PRIMARY_INDEX10.ID%TYPE;
  V_ID_CARD  PRIMARY_INDEX10.ID_CARD%TYPE;
  CURSOR TEMP_CURSOR IS
    SELECT T2.ID, T2.ID_CARD
      FROM VIRTUAL_CARD10 T1, PRIMARY_INDEX10 T2
     WHERE T1.ID_CARD = T2.ID_CARD
       AND T1.REMARK = '**市****區數據'
       AND T2.REMARK = '**市****區數據';
BEGIN
  OPEN TEMP_CURSOR;
  LOOP
    /* 取得一行遊標數據並放到對應變量中 */
    FETCH TEMP_CURSOR
      INTO V_INDEX_ID, V_ID_CARD;
    /* 若是沒有數據則退出 */
    EXIT WHEN TEMP_CURSOR%NOTFOUND;
    /* 業務邏輯 */
    UPDATE VIRTUAL_CARD10
       SET INDEX_ID = V_INDEX_ID
     WHERE ID_CARD = V_ID_CARD;
    /* 更新一次,+1 */
    V_COUNT := V_COUNT + 1;
    /* 1000條提交1次 */
    IF V_COUNT >= 1000 THEN
      COMMIT; --提交
      V_COUNT := 0; --重置
    END IF;
  END LOOP;
  COMMIT; -- 提交全部數據,把這個去掉,能夠查看是不是本身想要的效果,再決定是否提交
  CLOSE TEMP_CURSOR;
END;

  執行時間:blog

  10000條1提交,執行時間:

  方式四:顯式遊標+數組(更新一次提交一次)(使用BULK COLLECT)

/* 使用遊標+數組進行更新(更新一次提交一次) */
DECLARE
  /* 建立數組:一列多行 */
  TYPE TYPE_INDEX_ID IS TABLE OF PRIMARY_INDEX10.ID%TYPE;
  TYPE TYPE_ID_CARD IS TABLE OF PRIMARY_INDEX10.ID_CARD%TYPE;
  /* 起別名 */
  V_INDEX_ID TYPE_INDEX_ID;
  V_ID_CARD  TYPE_ID_CARD;
  /* 將查詢出來的數據放到遊標裏 */
  CURSOR TEMP_CURSOR IS
    SELECT T2.ID, T2.ID_CARD
      FROM VIRTUAL_CARD10 T1, PRIMARY_INDEX10 T2
     WHERE T1.ID_CARD = T2.ID_CARD
       AND T1.REMARK = '**市****區數據'
       AND T2.REMARK = '**市****區數據';
BEGIN
  OPEN TEMP_CURSOR;
  LOOP
    /* 取得1000行遊標數據並放到對應數組中,每次讀取1000條數據 */
    FETCH TEMP_CURSOR BULK COLLECT
      INTO V_INDEX_ID, V_ID_CARD LIMIT 1000;
    /* 若是沒有數據則退出 */
    EXIT WHEN TEMP_CURSOR%NOTFOUND;
    /* 遍歷數據 */
    FOR I IN V_INDEX_ID.FIRST .. V_INDEX_ID.LAST LOOP
      /* 業務邏輯 */
      UPDATE VIRTUAL_CARD10
         SET INDEX_ID = V_INDEX_ID(I)
       WHERE ID_CARD = V_ID_CARD(I);
      COMMIT;
    END LOOP;
  END LOOP;
  CLOSE TEMP_CURSOR;
END;

  執行時間:

  方式五: 顯式遊標+數組(1000條提交一次)(使用BULK COLLECT)

/* 使用遊標+數組進行更新(1000條提交一次) */
DECLARE
  /* 建立數組:一列多行 */
  TYPE TYPE_INDEX_ID IS TABLE OF PRIMARY_INDEX10.ID%TYPE;
  TYPE TYPE_ID_CARD IS TABLE OF PRIMARY_INDEX10.ID_CARD%TYPE;
  /* 起別名 */
  V_INDEX_ID TYPE_INDEX_ID;
  V_ID_CARD  TYPE_ID_CARD;
  /* 將查詢出來的數據放到遊標裏 */
  CURSOR TEMP_CURSOR IS
    SELECT T2.ID, T2.ID_CARD
      FROM VIRTUAL_CARD10 T1, PRIMARY_INDEX10 T2
     WHERE T1.ID_CARD = T2.ID_CARD
       AND T1.REMARK = '**市****區數據'
       AND T2.REMARK = '**市****區數據';
BEGIN
  OPEN TEMP_CURSOR;
  LOOP
    /* 取得1000行遊標數據並放到對應數組中 */
    FETCH TEMP_CURSOR BULK COLLECT
      INTO V_INDEX_ID, V_ID_CARD LIMIT 1000;
    /* 若是沒有數據則退出 */
    EXIT WHEN TEMP_CURSOR%NOTFOUND;
    /* 遍歷數據 */
    FOR I IN V_INDEX_ID.FIRST .. V_INDEX_ID.LAST LOOP --或者:FOR I IN 1 .. V_INDEX_ID.COUNT LOOP
      /* 業務邏輯 */
      UPDATE VIRTUAL_CARD10
         SET INDEX_ID = V_INDEX_ID(I)
       WHERE ID_CARD = V_ID_CARD(I);
      IF I >= V_INDEX_ID.LAST THEN
        COMMIT; --提交
      END IF;
    END LOOP;
  END LOOP;
  CLOSE TEMP_CURSOR;
END;

  執行時間:

  方式六:推薦使用(使用BULK COLLECT和FORALL)

/* 使用遊標+數組進行更新(BULK COLLECT和FORALL) */
DECLARE
  /* 建立數組:一列多行 */
  TYPE TYPE_INDEX_ID IS TABLE OF PRIMARY_INDEX10.ID%TYPE;
  TYPE TYPE_ID_CARD IS TABLE OF PRIMARY_INDEX10.ID_CARD%TYPE;
  /* 起別名 */
  V_INDEX_ID TYPE_INDEX_ID;
  V_ID_CARD  TYPE_ID_CARD;
  /* 將查詢出來的數據放到遊標裏 */
  CURSOR TEMP_CURSOR IS
    SELECT T2.ID, T2.ID_CARD
      FROM VIRTUAL_CARD10 T1, PRIMARY_INDEX10 T2
     WHERE T1.ID_CARD = T2.ID_CARD
       AND T1.REMARK = '**市****區數據'
       AND T2.REMARK = '**市****區數據';
BEGIN
  OPEN TEMP_CURSOR;
  LOOP
    /* 取得1000行遊標數據並放到對應數組中 */
    FETCH TEMP_CURSOR BULK COLLECT
      INTO V_INDEX_ID, V_ID_CARD LIMIT 1000;
    /* 若是沒有數據則退出 */
    EXIT WHEN TEMP_CURSOR%NOTFOUND;
    /* 遍歷數據 */
    FORALL I IN 1 .. V_INDEX_ID.COUNT-- 或者V_INDEX_ID.FIRST .. V_INDEX_ID.LAST
    /* 業務邏輯 */
      UPDATE VIRTUAL_CARD10
         SET INDEX_ID = V_INDEX_ID(I)
       WHERE ID_CARD = V_ID_CARD(I);
    COMMIT; --提交
  END LOOP;
  CLOSE TEMP_CURSOR;
END;

  執行時間:

  從Oracle8開始,oracle爲PL/SQL引入了兩個新的數據操縱語言(DML)語句:BULK COLLECT和FORALL。

  這兩個語句在PL/SQL內部進行一種數組處理;BULK COLLECT提供對數據的高速檢索,FORALL可大大改進INSERT、UPDATE和DELETE操做的性能。

  Oracle數據庫使用這些語句大大減小了PL/SQL與SQL語句執行引擎的環境切換次數,從而使其性能有了顯著提升。 

小結:

  數據量小的時候能夠用方式二,數據量大的時候推薦使用方式六;

  必定要建索引。

 

寫在最後

  哪位大佬如若發現文章存在紕漏之處或須要補充更多內容,歡迎留言!!!

 相關推薦:

相關文章
相關標籤/搜索