我有一个关于在SQL中构造查询的一般性问题。
我有一个数据库,我需要为它提取过去n天的数据,这被证明是非常庞大的,无法放入内存中。
我有一个类似这样的查询
SELECT * FROM DB_A
WHERE data >=n days将查询放入内存的最好方法是使用循环,这样我就可以一次提取数据,比如m天(其中m << n)
那么我该怎么做呢?
我一点也不明白。
发布于 2020-05-30 15:17:46
如果您使用的是Oracle 12c或更高版本,则可以通过使用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;一直持续到最后……
发布于 2020-05-30 18:56:24
如果数据量太大,无法作为一个数据量处理,则在分界日期上循环显式:
SELECT * FROM DB_A
WHERE date_column >= DATE_FROM and date_column < DATE_TO请注意,性能(也就是内存消耗)不仅与结果行数相关,还与处理数据的方式相关。
尽量避免在大容量数据上出现游标循环。首选的处理方式不是逐行的,而是面向集合的,即
INSERT .... SELECT .... FROM DB_A WHERE ...如果源表DB_A按相关的date列(上面查询中的date_column)进行分区,您可能会从中获利。查询将只选择那些具有受限制日期的分区。
您可以(在使用SQL方法的同时)从parallel DML中获益,从而提高性能。
示例
假设表DB_A的date_column列中包含30天的数据,并且您希望以5天为一批处理这些数据。
您将以6个批次结束,如下面的查询所示。
查询首先从表中选择不同的截断日期,然后使用ROW_NUMBER计算batch_idx,并使用trunc除以5。最后,计算每个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 https://stackoverflow.com/questions/62098783
复制相似问题