Как выполнить массовое обновление в PL / SQL Oracle с зависимостями - PullRequest
0 голосов
/ 22 октября 2018

В настоящее время я запускаю следующие отдельные обновления в PL / SQL Oracle 12.1, и мне нужно знать, как повысить производительность с помощью массового обновления, поскольку для их завершения требуется несколько часов или любой другой стратегии в этом отношении.

Проблема заключается в том, что мне нужно обновить несколько столбцов с идентичным набором условий (этих CASE WHEN операторов) из таблицы (LARGE_TBL), в которой есть несколько сотен тысяч записей (в самой MAIN_TBL есть несколькосотни тысяч записей. Обе таблицы имеют индексы LT_ID и MT_ID).

Существует несколько других UPDATES с разными значениями для LT.IDX_2 и MT.IDX_2 (для краткости я их исключил) и отображается только IDX_2 = G (есть другие идентичные UPDATE с другими IDX_2 значениями).

UPDATE MAIN_TBL MT
SET
    MT.STOP_FLAG = (  
        SELECT 
            CASE
                WHEN 
                    NOT EXISTS (SELECT 1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G')
                    OR (SELECT LT.COL_1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') IS NULL
                    OR (SELECT LT.COL_1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') <> 'Y'  
                THEN 'SF01'
                ELSE MT.STOP_FLAG
            END
        FROM DUAL
    ),
    MT.ES = (  
        SELECT 
            CASE
                WHEN 
                    NOT EXISTS (SELECT 1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G')
                    OR (SELECT LT.COL_1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') IS NULL
                    OR (SELECT LT.COL_1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') <> 'Y'
                THEN 'E'
                ELSE MT.ES
            END
        FROM DUAL
    ),
    MT.PW = (  
        SELECT 
            CASE
                WHEN 
                    NOT EXISTS (SELECT 1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G')
                    OR (SELECT LT.COL_1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') IS NULL
                    OR (SELECT LT.COL_1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') <> 'Y'
                THEN 'W'
                ELSE MT.PW
            END
        FROM DUAL
    ),
    MT.UPDATE_DT = SYSDATE
WHERE 
    MT.STOP_FLAG IS NULL
    AND MT.IDX_2 = 'G'
    AND MT.ES IS NULL
    AND MT.SS = 'C'
    AND MT.PW = 'A';


UPDATE MAIN_TBL MT
SET
    MT.STOP_FLAG = (  
        SELECT 
            CASE
                WHEN 
                    NOT EXISTS (SELECT 1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G')
                    OR (SELECT LT.COL_2 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') IS NULL
                    OR (SELECT LT.COL_2 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') <> 'Y'  
                THEN 'SF02'
                ELSE MT.STOP_FLAG
            END
        FROM DUAL
    ),
    MT.ES = (  
        SELECT 
            CASE
                WHEN 
                    NOT EXISTS (SELECT 1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G')
                    OR (SELECT LT.COL_2 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') IS NULL
                    OR (SELECT LT.COL_2 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') <> 'Y'
                THEN 'E'
                ELSE MT.ES
            END
        FROM DUAL
    ),
    MT.PW = (  
        SELECT 
            CASE
                WHEN 
                    NOT EXISTS (SELECT 1 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G')
                    OR (SELECT LT.COL_2 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') IS NULL
                    OR (SELECT LT.COL_2 FROM LARGE_TBL LT WHERE LT.LT_ID = MT.MT_ID AND LT.IDX_2 = 'G') <> 'Y'
                THEN 'W'
                ELSE MT.PW
            END
        FROM DUAL
    ),
    MT.UPDATE_DT = SYSDATE
WHERE 
    MT.STOP_FLAG IS NULL
    AND MT.IDX_2 = 'G'
    AND MT.ES IS NULL
    AND MT.SS = 'C'
    AND MT.PW = 'A';

Проблема в том, что, например, в случае выше, второй UPDATEзависит от первого UPDATE, потому что второе UPDATE должно выполняться только если MT.STOP_FLAG IS NULL.Поэтому, если MT.STOP_FLAG установлено с первым UPDATEMT.STOP_FLAG = SF01), второе UPDATE не должно выполняться, так как предложение WHERE не будет выполнено (MT.STOP_FLAG IS NULL).Другими словами, порядок выполнения этих UPDATE имеет значение.

Я не использовал функцию массового обновления PL / SQL, поэтому я не уверен, как к этому подойти.Должен ли я создать курсор для извлечения всех необходимых столбцов из таблицы LARGE_TBL с соответствующими условиями, например, IDX_2 = G или IDX_2 = R, которая является относительно большой таблицей (несколько сотен тысяч записей), а затем извлечь их с помощью BULK COLLECTв несколько определенных TYPE и, наконец, использовать один FORALL с несколькими отдельными операторами UPDATE?Или несколько FORALL, по одному для каждого UPDATE?

Если использовать курсоры, как мне обработать первый элемент в моем операторе CASE WHEN, где мне нужно убедиться, что запись существует?или нет?

Ответы [ 2 ]

0 голосов
/ 22 октября 2018

Ваши условия могут быть эффективно переписаны как проверка того, что значение поля lt.col_1 не равно 'Y' (т. Е. lt.col_1 is null or lt.col_1 != 'Y').Я создал быстрый тестовый пример, чтобы продемонстрировать, что это так, используя ваш старый способ проверки и новый:

WITH t1 AS (SELECT 1 mt_id, 10 val FROM dual UNION ALL
            SELECT 2 mt_id, 20 val FROM dual UNION ALL
            SELECT 3 mt_id, 30 val FROM dual UNION ALL
            SELECT 4 mt_id, 40 val FROM dual UNION ALL
            SELECT 5 mt_id, 50 val FROM dual),
     t2 AS (SELECT 2 lt_id, 'F' idx_2, NULL col_1 FROM dual UNION ALL
            SELECT 3 lt_id, 'G' idx_2, NULL col_1 FROM dual UNION ALL
            SELECT 4 lt_id, 'G' idx_2, 'N' col_1 FROM dual UNION ALL
            SELECT 5 lt_id, 'G' idx_2, 'Y' col_1 FROM dual)
SELECT 'new_way' qry,
       t1.mt_id,
       t1.val,
       CASE WHEN t2.col_1 is null or t2.col_1 != 'Y' THEN 'SF01' END new_stop_val
FROM   t1
       LEFT OUTER JOIN t2 ON t1.mt_id = t2.lt_id AND idx_2 = 'G'
UNION ALL
SELECT 'old_way' qry,
       t1.mt_id,
       t1.val,
       CASE WHEN NOT EXISTS (SELECT 1 FROM t2 WHERE t2.LT_ID = t1.MT_ID AND t2.IDX_2 = 'G')
                 OR (SELECT t2.COL_1 FROM t2 WHERE t2.LT_ID = t1.MT_ID AND t2.IDX_2 = 'G') IS NULL
                 OR (SELECT t2.COL_1 FROM t2 WHERE t2.LT_ID = t1.MT_ID AND t2.IDX_2 = 'G') <> 'Y'
                 THEN 'SF01'
       END new_stop_val
FROM   t1
ORDER BY mt_ID, qry;

QRY          MT_ID        VAL NEW_STOP_VAL
------- ---------- ---------- ------------
new_way          1         10 SF01
old_way          1         10 SF01
new_way          2         20 SF01
old_way          2         20 SF01
new_way          3         30 SF01
old_way          3         30 SF01
new_way          4         40 SF01
old_way          4         40 SF01
new_way          5         50 
old_way          5         50 

Теперь, когда мы можем свернуть проверки таблицы large_table в одну проверку,затем мы можем проверить другие столбцы в large_table в одном операторе case.Это означает, что вам больше не нужны отдельные операторы обновления.Вы можете сделать это в одном операторе слияния следующим образом:

MERGE INTO main_table tgt
USING (SELECT mt.rowid, r_id,
              CASE WHEN lt.col_1 is null or lt.col_1 != 'Y' THEN 'SF01'
                   WHEN lt.col_2 is null or lt.col_2 != 'Y' THEN 'SF02'
                   ELSE mt.stop_flag -- null
              END new_stop_flag,
              CASE WHEN NVL(lt.col1, 'N') != 'Y' THEN 'E'
                   WHEN NVL(lt.col2, 'N') != 'Y' THEN 'E'
                   ELSE mt.es -- null
              END new_es,
              CASE WHEN NVL(lt.col_1, 'N') != 'Y' THEN 'W'
                   WHEN NVL(lt.col_2, 'N') != 'Y' THEN 'W'
                   ELSE mt.pw
              END new_pw
       FROM   main_table mt
              LEFT JOIN large_table lt ON (mt.mt_id = lt.lt_id AND lt.idx_2 = 'G')
       WHERE  mt.stop_flag IS NULL
       AND    mt.idx_2 = 'G'
       AND    mt.es IS NULL
       AND    mt.ss = 'C'
       AND    mt.pw = 'A') src
  ON (tgt.rowid = src.r_id)
WHEN MATCHED THEN
  UPDATE tgt.stop_flag = src.new_stop_flag,
         tgt.es = src.es,
         tgt.pw = src.pw;
0 голосов
/ 22 октября 2018

Ваше утверждение UPDATE выглядит довольно странно, попробуйте переписать его.

Если у вас есть обновление, например

UPDATE MAIN_TBL MT
SET MT.STOP_FLAG = (  
    SELECT 
        CASE 
            WHEN {whatever condition}
            THEN 'SF01'
            ELSE MT.STOP_FLAG
        END
    FROM DUAL
)

, то оно в основном такое же, как

UPDATE MAIN_TBL MT
SET MT.STOP_FLAG = 'SF01'
WHERE {whatever condition}

Следующие примеры, скорее всего, не работают, но они должныдать вам подсказку, как такие обновления могли бы быть написаны намного лучше.

UPDATE MAIN_TBL MT
SET
    MT.STOP_FLAG = 'SF01',
    MT.ES = 'E',
    MT.PW = 'W'
    MT.UPDATE_DT = SYSDATE
WHERE 
    MT.STOP_FLAG IS NULL
    AND MT.IDX_2 = 'G'
    AND MT.ES IS NULL
    AND MT.SS = 'C'
    AND MT.PW = 'A'
    AND NOT EXISTS (
        SELECT 1 
        FROM LARGE_TBL LT 
        WHERE LT.LT_ID = MT.MT_ID 
           AND (LT.IDX_2 = 'G' OR LT.COL_1 <> 'Y' OR LT.COL_1 IS NULL)
        );


UPDATE 
    (SELECT MT.*
    FROM MAIN_TBL MT
        JOIN LARGE_TBL LT ON LT.LT_ID = MT.MT_ID
    WHERE LT.IDX_2 = 'G' OR LT.COL_1 <> 'Y' OR LT.COL_1 IS NULL)
SET
    MT.STOP_FLAG = 'SF01',
    MT.ES = 'E',
    MT.PW = 'W'
    MT.UPDATE_DT = SYSDATE
WHERE 
    MT.STOP_FLAG IS NULL
    AND MT.IDX_2 = 'G'
    AND MT.ES IS NULL
    AND MT.SS = 'C'
    AND MT.PW = 'A'

Если у вас есть обновление типа

UPDATE MAIN_TBL MT
SET MT.STOP_FLAG = (  
    SELECT 
        CASE 
            WHEN {whatever condition}
            THEN 'SF01'
            ELSE MT.STOP_FLAG
        END
    FROM DUAL
)

, то оно в основном совпадает с

UPDATE MAIN_TBL MT
SET MT.STOP_FLAG = 'SF01'
WHERE {whatever condition}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...