Оптимальное решение для усеченного стола - PullRequest
1 голос
/ 13 апреля 2019

Я создал рабочий процесс весенней интеграции, который загружает данные из csv в базу данных oracle. Это кластерная среда, в которой каждый узел обрабатывает один CSV-файл и загружает данные во временную таблицу.

Структура временной таблицы: (индекс по AccountNumber)

ID
AccountNumber
ItemId
Value

У меня есть конфигурация rabbitmq для весенней интеграции, которая публикует имена файлов в очереди. Каждый узел в кластере выбирает только один файл, читает csv из файловой системы (общая файловая система и база данных Oracle) и загружает данные в таблицу TEMP. (Размер каждого CSV составляет 2 ГБ).

После загрузки всех данных во временную таблицу один узел должен переместить данные из временной таблицы в основную таблицу, которая имеет ту же структуру, что и временная таблица.

Основная таблица: (индекс по Account Number)

Id
AccountNumber
ItemId
Value

Я создал хранимую процедуру, которая удаляет существующие номера учетных записей из основной таблицы и загружает учетные записи в основной таблице.

После перемещения данных в основную таблицу я удаляю данные из временной таблицы в конце процедуры.

Мой вопрос заключается в том, какой оптимальный способ обрезать эту таблицу.

Проблема: предположим, у меня есть эта запись в моей основной таблице.

Основной стол:

Account Number   ItemId   ItemValue
-----------------------------------
123456             5        XYZ
123456             6        ABC
123456             7        DEF

Теперь я получаю эту запись из таблицы csv во временную таблицу:

AccountNumber    ItemId    ItemValue
------------------------------------
123456             5        FGH

теперь моя главная таблица должна иметь только одно значение. Строки с ItemId 6 и 7 должны быть удалены.

Account Number   ItemId    ItemValue
-------------------------------------
123456             5        FGH

Могу ли я достичь этого слиянием с?

Сценарий 1 :

Было бы лучше обрезать эту таблицу перед загрузкой данных в таблицу TEMP? (Две отдельные транзакции базы данных, одна для усеченной таблицы и другая для перемещения данных).

(вызывается перед публикацией имен файлов в очереди) одна процедура для очистки временной таблицы партиями перед загрузкой.

Шаг 1:

create or replace procedure CleanTempTable
IS
v_numberRows int :=20000;

BEGIN
loop
Delete from TEMP where rownum <= v_numberRows;
EXIT WHEN SQL%ROWCOUNT = 0;
commit;
END LOOP;
END;
/

Одна процедура для перемещения данных из временной в главную.

Это вызывает в конце на этапе консолидации.

CREATE OR REPLACE PROCEDURE LOAD_DATA_TO_CONSOLIDATE (updatecount OUT NUMBER )
IS
  cnt number := 0;
  account_num MAIN_TABLE.ACCOUNT_NO%TYPE;
  CURSOR account_cursor IS
    SELECT distinct ACCOUNT_NO from TEMP_TABLE;
BEGIN
OPEN account_cursor;
    LOOP
        FETCH account_cursor INTO account_num;
        EXIT WHEN account_cursor%NOTFOUND;
        delete from MAIN where ACCOUNT_NO = account_num;
    insert into MAIN(ID,ACCOUNT_NO,FACT_ID,FACT_VALUE) select HIBERNATE_SEQUENCE.nextval,temp.ACCOUNT_NO,temp.VALUE from TEMP temp
    where ACCOUNT_NO = account_num;
        cnt := cnt + sql%rowcount;
    commit;
    END LOOP;
    updatecount := cnt;
    CLOSE account_cursor;
END LOAD_DATA_TO_CONSOLIDATE;

Сценарий 2:

было бы лучше обрезать эту таблицу после перемещения данных из таблицы TEMP в основную таблицу (все внутри одной хранимой процедуры (в одной транзакции БД))

CREATE OR REPLACE PROCEDURE LOAD_DATA_TO_CONSOLIDATE (updatecount OUT NUMBER )
IS
  cnt number := 0;
  account_num MAIN_TABLE.ACCOUNT_NO%TYPE;
  CURSOR account_cursor IS
    SELECT distinct ACCOUNT_NO from TEMP_TABLE;
BEGIN
OPEN account_cursor;
    LOOP
        FETCH account_cursor INTO account_num;
        EXIT WHEN account_cursor%NOTFOUND;
        delete from MAIN where ACCOUNT_NO = account_num;
    insert into MAIN(ID,ACCOUNT_NO,FACT_ID,FACT_VALUE) select HIBERNATE_SEQUENCE.nextval,temp.ACCOUNT_NO,temp.VALUE from TEMP temp
    where ACCOUNT_NO = account_num;
        cnt := cnt + sql%rowcount;
    commit;
    END LOOP;
    updatecount := cnt;
    CLOSE account_cursor;
delete from TEMP; //removing all data
END LOAD_DATA_TO_CONSOLIDATE;

1 Ответ

0 голосов
/ 13 апреля 2019

С моей точки зрения, Oracle, ни один из предложенных вами вариантов. И вот почему:

  • вы удваиваете работу, сначала загружая данные во временную таблицу, а затем копируя их в основную таблицу
  • построчная обработка медленная за медленной
  • фиксация внутри цикла может привести к ошибке ORA-01555
  • удаление (команда DELETE) всегда медленнее усечения (TRUNCATE)

Предложение: чтобы избежать временной таблицы, используйте функцию внешние таблицы , которая использует ваш CSV-файл, как если бы это была обычная таблица Oracle.

Это означает, что все можно было сделать всего за два утверждения:

-- Delete rows from the MAIN table whose ACCOUNT_NO exists in the CSV file
delete from main m
where exists (select null 
              from external_table t
              where t.account_no = m.account_no
             );

-- Insert rows into the MAIN table
insert into main (col1, col2, ...)
select col1, col2 from external_table;

В качестве альтернативы - что, вероятно, будет работать лучше всего - вы можете UPDATE значения для ACCOUNT_NO, который уже существует в таблице и INSERT только строки, которые не существуют; вместо двух команд используйте один быстрый оператор MERGE (также известный как upsert )

merge into main m
  using (select t.account_no, t.col1, t.col2, ...
         from external_table t
        ) x
on m.account_no = x.account_no
when matched then update set m.col1 = x.col1,
                             m.col2 = x.col2, ...
when not matched then insert (account_no, col1, col2, ...)
                      values (x.account_no, x.col1, x.col2, ...);

С MERGE вы не загружаете здесь, удаляете оттуда, вставляете отсюда туда ... довольно аккуратно и, как я уже сказал, быстро. Нет необходимости в PL / SQL.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...