Нужен совет о лучшем способе фильтрации текстовых файлов на основе справочной таблицы - PullRequest
1 голос
/ 24 февраля 2012

В настоящее время я ищу способ оптимизировать процесс, который
занимает много времени, чтобы бежать.

  • Существует около 270 текстовых файлов для фильтрации.
  • Каждый файл имеет около 70k ~ 150k строк.
  • В справочной таблице обычно содержится около 16m записей в Oracle 10g.
  • Процесс выполняется каждый час.
  • Есть вероятность, что 9 экземпляров этого процесса могут быть запущены почти
    simultaneaously.

Что я сейчас делаю, это помещаю справочную таблицу в файл, копирую ее в
хеш, сделайте то же самое с текстовым файлом, затем сделайте совпадение ключа хеша.
Любая запись в текстовом файле, найденная в списке литературы, будет отброшена.

Это повторяется для всех 270 файлов, однако часть спулинга выполняется только
один раз в начале.

Однако этот подход потребляет около 300 МБ ~ 500 МБ ОЗУ, а с
возможность запуска нескольких экземпляров этого процесса почти на
в то же время, его кошмар на наш сервер.

Есть идеи, как сделать это лучше?

Ответы [ 2 ]

3 голосов
/ 24 февраля 2012

Это похоже на то, что можно эффективно сделать в базе данных (большое противодействие объединению).

Вы можете использовать UTL_FILE , чтобы прочитать файлы и сравнить их непосредственно с вашей справочной таблицей,Вы можете использовать функцию-обертку для непосредственного выбора файла:

CREATE OR REPLACE TYPE tab_varchar AS TABLE OF VARCHAR2(4000);
/

CREATE OR REPLACE FUNCTION select_file(p_dir VARCHAR2, p_file VARCHAR2)
   RETURN tab_varchar
   PIPELINED IS
   l_file utl_file.file_type := utl_file.fopen(p_dir, p_file, 'r', 4000);
   l_line VARCHAR2(4000);
BEGIN
   LOOP
      BEGIN
         utl_file.get_line(l_file, l_line);
      EXCEPTION
         WHEN No_Data_Found THEN
            EXIT;
      END;
      PIPE ROW (l_line);
   END LOOP;
   utl_file.fclose(l_file);
   RETURN;
END;
/

Это позволит вам напрямую выбрать ваши данные, например, это вернет содержимое файла C:\tmp\a.txt

create directory tmp_dir as 'C:\tmp\';

select column_value from table(select_file('TMP_DIR', 'a.txt'));

Отсюда для каждого файла должен быть в порядке один запрос, например:

INSERT INTO ref_table r
   (SELECT column_value
      FROM table(select_file(:dir, :file_name)
     WHERE column_value NOT IN (SELECT ref_value
                                  FROM ref_table
                                 WHERE ref_value IS NOT NULL)
       AND column_value IS NOT NULL);
1 голос
/ 24 февраля 2012

Я бы посоветовал вам загружать только данные БД в память и обрабатывать файлы, например (извините, это псевдокод, но вы должны понять и реализовать его в perl):

HashSet dbData = GetDataFromDB();
foreach(filename in filenames) {
    FileHandle handle = OpenRead(filename);
    FileHandle tmphandle = OpenWrite(filename + ".tmp");
    while(string line = handle.ReadLine()) {
        if(!dbData.Contains(line)) {
            tmphandle.Write(line);
        }
    }
    tmphandle.Flush();
    tmphandle.Close();
    handle.Close();
    Delete(filename);
    Rename(tmpfilename, filename);
}

Это займет примерно столько же ОЗУ, сколько занимает ваша справочная таблица.

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