Зацикливание на запросе SQL QUERY из огромной базы данных для получения дополнительных данных - PullRequest
0 голосов
/ 30 мая 2020

У меня общий c вопрос о структурировании запроса в SQL.

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

У меня есть запрос типа

 SELECT * FROM DB_A 
  WHERE data >=n days

Лучший способ поместить запрос в память - использовать al oop, чтобы я мог извлекать данные, скажем, m дней во времени ( where m << n) </p>

Итак, как я могу это сделать?

Я не понимаю.

Ответы [ 2 ]

1 голос
/ 30 мая 2020

Если вы используете Oracle 12 c или выше, вы можете напрямую использовать разбиение на страницы, используя OFFSET и FETCH следующим образом:

SELECT * FROM DB_A 
  WHERE data >=n days
ORDER BY n
OFFSET m*x rows
fetch next m rows only;

здесь, x - количество итераций, начиная с с 0.

Допустим, вам нужно 5 строк за раз.

для первой итерации:

OFFSET 0 rows
fetch next 5 rows only;

для второй итерации:

OFFSET 5 rows
fetch next 5 rows only;

Продолжать до конца .....

0 голосов
/ 30 мая 2020

Если объем данных слишком велик для обработки как единое целое, l oop укажите в датах разграничения:

 SELECT * FROM DB_A 
 WHERE date_column >= DATE_FROM  and date_column < DATE_TO

Обратите внимание, что для производительности (т.е. также для потребления памяти) не важно только количество строк результата, но способ обработки данных .

Старайтесь избегать курсора l oop на данных большого объема. Предпочтительным способом обработки является не строка за строкой , а ориентированная на набор , т.е.

INSERT ....  SELECT .... FROM DB_A WHERE ...

Вы можете получить прибыль, если исходная таблица DB_A будет секционировано в соответствующем столбце даты (date_column в запросе выше). Запрос выберет только те разделы с ограниченными датами.

Вы можете получить прибыль (при использовании подхода SQL) от параллельного DML , чтобы повысить производительность.

Пример

Предположим, что таблица DB_A содержит данные за 30 дней в столбце date_column, и вы хотите обрабатывать данные в 5-дневных пакетах .

У вас будет 6 пакетов, как показано в запросе ниже.

Запрос выбирает отдельные усеченные дни из таблицы в первую очередь, а затем вычисляет batch_idx с использованием ROW_NUMBER и делением на 5 с помощью trunc. Наконец, вычисляются граничные даты для batch_idx.

with batch1 as
(select distinct trunc(date_column)  start_date from db_a),
batch2 as (
select start_date,
  trunc((row_number() over (order by start_date)-1) / 5) as batch_idx
from batch1)
select batch_idx, min(START_DATE) START_DATE, max(START_DATE)+1 END_DATE from batch2
group by batch_idx
order by 1
;

 BATCH_IDX START_DATE          END_DATE           
---------- ------------------- -------------------
         0 01.05.2020 00:00:00 06.05.2020 00:00:00
         1 06.05.2020 00:00:00 11.05.2020 00:00:00
         2 11.05.2020 00:00:00 16.05.2020 00:00:00
         3 16.05.2020 00:00:00 21.05.2020 00:00:00
         4 21.05.2020 00:00:00 26.05.2020 00:00:00
         5 26.05.2020 00:00:00 31.05.2020 00:00:00

Тот же самый запрос, который вы можете использовать в курсоре L OOP для обработки данных, как показано ниже.

Обратите внимание, что START_DATE является включительным, но END_DATE является исключительным.

BEGIN
 FOR cur in (
 with batch1 as
  (select distinct trunc(date_column)  start_date from db_a),
 batch2 as (
  select start_date,
   trunc((row_number() over (order by start_date)-1) / 5) as batch_idx
  from batch1)
  select batch_idx, min(START_DATE) START_DATE, max(START_DATE)+1 END_DATE from batch2
  group by batch_idx
  order by 1) 
LOOP
    dbms_output.put_line('Processing dates from '||to_char(cur.START_DATE,'dd.mm.yyyy') || ' to ' || to_char(cur.END_DATE,'dd.mm.yyyy'));
    insert into DB_TARGET(date_column,....) 
    select date_column,.... from DB_A 
    where date_column >= cur.START_DATE and date_column < cur.END_DATE;
    commit;
END LOOP;
END;
/

l oop будет выполняться шесть раз с ожидаемыми диапазонами дат:

Processing dates from 01.05.2020 to 06.05.2020
Processing dates from 06.05.2020 to 11.05.2020
Processing dates from 11.05.2020 to 16.05.2020
Processing dates from 16.05.2020 to 21.05.2020
Processing dates from 21.05.2020 to 26.05.2020
Processing dates from 26.05.2020 to 31.05.2020 
...