在一个sql查询循环从一个巨大的数据库做增量数据拉

qc6wkl3g  于 2021-08-13  发布在  Java
关注(0)|答案(2)|浏览(332)

我有一个关于在sql中构造查询的一般性问题。
我有一个数据库,我需要拉过去n天的数据,结果是巨大的,以适应内存。
我有个问题

SELECT * FROM DB_A 
  WHERE data >=n days

将查询适配到内存中的最佳方法是使用循环,这样我就可以每次提取m天的数据(其中m<<n)
那我怎么做呢?
我一点也不知道。

jmo0nnb3

jmo0nnb31#

如果您使用的是oracle 12c或更高版本,则可以按如下所示使用偏移量和获取来直接使用分页:

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;

一直到最后。。。。。

cu6pst1q

cu6pst1q2#

如果数据量太大,无法作为一个整体进行处理,请在分界日期上循环说明:

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

请注意,性能(即内存消耗)不仅与结果行的数量有关,还与处理数据的方式有关。
尽量避免在大容量数据上出现游标循环。首选的处理方式不是逐行,而是面向集合的,即。

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

如果源表 DB_A 在相关的日期列上分区( date_column 在上面的查询中)。查询将只选择那些日期受限的分区。
您可以从并行dml中获益(同时使用sql方法)来提高性能。
例子
假设这张table 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

您可以在游标循环中使用这个查询来处理数据,如下所示。
请注意 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;
/

循环将以预期的日期范围执行六次:

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

相关问题