Oracle SQL:如何最好地去计数多少值是在时间间隔?数据库查询vs. pandas(或更高效的库)?



我目前必须把我的头围绕编程以下任务。情况:假设我们有一个列,其中有时间数据(年-月-日-小时-分钟)。我们的程序将获得输入(工作日、开始时间、结束时间、时隙),我们希望返回值最小的间隔(由时隙指定)。要了解更多信息,该数据库有数百万个条目。因此我们的程序将被指定为

def calculate_optimal_window(weekday, starttime, endtime, timeslot):
return optimal_window

示例:假设我们想输入

weekday = Monday, starttime = 10:00, endtime = 12:00, timeslot = 30 minutes.

在这里,我们想要计算在10:00到12:00之间有多少条目,并计算每个30分钟槽(即10:00 - 10:30,10:01 - 10:31等)中的值的数量,并最终返回值最小的槽。您将如何制定一个有效的查询?

因为我正在使用Oracle SQL数据库,我的第二个问题是:使用像Dask或Vaex这样的库来完成过滤和计数会更有效吗?这种情况下的瓶颈在哪里?

如果配方太模糊,我很乐意提供更多信息。

祝一切顺利。

这部分:

因为我正在使用Oracle SQL数据库,我的第二个问题是:使用像Dask或Vaex这样的库会更有效率吗过滤和计数完成了吗?瓶颈在哪里情况?

根据你的服务器规格和集群/机器,你有可用的Dask,这是相当可能的瓶颈在你的分析将是在SQL和Dask工作者之间的数据传输,即使在(可能的)情况下,这可以有效地并行化。从DB的角度来看,选择数据并将其序列化可能至少与在相对较少的时间bin中计数一样昂贵。

在将分析转移到Dask之前,我会首先调查单独使用SQL的过程需要多长时间,以及这是否可以接受。通常的规则将适用:在时间索引上有良好的索引和分片。

您至少应该在SQL查询中执行基本的过滤和计数。通过一个简单的谓词,Oracle可以决定是使用索引还是分区,并可能减少数据库处理时间。发送更少的行将显著减少网络开销。

例如:

select trunc(the_time, 'MI') the_minute, count(*) the_count
from test1
where the_time between timestamp '2021-01-25 10:00:00' and timestamp '2021-01-25 11:59:59'
group by trunc(the_time, 'MI')
order by the_minute desc;
这些查询中最棘手的部分可能是off-by- 1问题。你是真的想要"10:00到12:00",还是想要"10:00到11:59:59"?

可以选择在SQL中执行整个计算。我敢打赌SQL版本会稍微快一点,同样是因为网络开销。但是发送一个结果行和发送120个汇总行可能不会有明显的区别,除非这个查询经常被执行。

在这一点上,问题转向了一个更主观的问题,即在哪里放置"业务逻辑"。我敢打赌,大多数程序员更喜欢你的Python解决方案,而不是我的查询。但是在SQL中完成所有工作的一个小优点是将所有奇怪的日期逻辑保存在一个地方。如果您分多个步骤处理结果,则更有可能出现差1的错误。
--Time slots with the smallest number of rows.
--(There will be lots of ties because the data is so boring.)
with dates as
(
--Enter literals or bind variables here:
select
cast(timestamp '2021-01-25 10:00:00' as date) begin_date,
cast(timestamp '2021-01-25 11:59:59' as date) end_date,
30 timeslot
from dual
)
--Choose the rows with the smallest counts.
select begin_time, end_time, total_count
from
(
--Rank the time slots per count.
select begin_time, end_time, total_count,
dense_rank() over (order by total_count) smallest_when_1
from
(
--Counts per timeslot.
select begin_time, end_time, sum(the_count) total_count
from
(
--Counts per minute.
select trunc(the_time, 'MI') the_minute, count(*) the_count
from test1
where the_time between (select begin_date from dates) and (select end_date from dates)
group by trunc(the_time, 'MI')
order by the_minute desc
) counts
join
(
--Time ranges.
select
begin_date + ((level-1)/24/60) begin_time,
begin_date + ((level-1)/24/60) + (timeslot/24/60) end_time
from dates
connect by level <=
(
--The number of different time ranges.
select (end_date - begin_date) * 24 * 60 - timeslot + 1
from dates
)
) time_ranges
on the_minute between begin_time and end_time
group by begin_time, end_time
)
)
where smallest_when_1 = 1
order by begin_time;

您可以在这里运行一个数据库<>fiddle。

最新更新