如何解决Oracle SQL:如何最好地计算时间间隔中有多少个值?数据库查询与 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 的角度来看,选择数据并将其序列化可能至少与在相对较少的时间段中进行计数一样昂贵。
在将分析转移到 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;
(这些查询中最棘手的部分可能是一对一问题。你真的想要“10:00 到 12:00 之间”,还是“10:00 到 11:59:59 之间”? “?)
或者,您可以在 SQL 中执行整个计算。我敢打赌 SQL 版本会稍微快一点,同样是因为网络开销。但是,除非经常执行此查询,否则发送一个结果行与 120 个聚合行可能不会产生显着差异。
此时,问题转向更主观的问题,即“业务逻辑”放在哪里。我敢打赌大多数程序员更喜欢你的 Python 解决方案而不是我的查询。但是在 SQL 中完成所有工作的一个小优势是将所有奇怪的日期逻辑放在一个地方。如果您在多个步骤中处理结果,则出现一对一错误的可能性更大。
--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,total_count,dense_rank() over (order by total_count) smallest_when_1
from
(
--Counts per timeslot.
select begin_time,sum(the_count) total_count
from
(
--Counts per minute.
select trunc(the_time,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;
您可以运行 dbfiddle here。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。