1

我目前必须全神贯注地编写以下任务。情况:假设我们有一列有时间数据(年-月-日时-分)。我们的程序将获取输入(工作日、开始时间、结束时间、时间段),并且我们希望返回值最少的间隔(由时间段指定)。有关更多信息,该数据库有数百万个条目。所以我们的程序将被指定为

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 等库来完成过滤和计数会更有效吗?这种情况下的瓶颈在哪里?

如果配方太模糊,很高兴提供更多信息。

一切顺利。

4

2 回答 2

1

这部分:

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

根据您的服务器规格和可用于 Dask 的集群/机器,您分析中的瓶颈很可能是 SQL 和 Dask 工作人员之间的数据传输,即使在(可能的)情况下也是如此有效地并行化。从数据库的角度来看,选择数据并对其进行序列化可能至少与在相对较少的时间箱中计数一样昂贵。

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

于 2021-01-26T20:30:41.677 回答
0

您至少应该在 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, 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;

你可以在这里运行一个 db<>fiddle 。

于 2021-01-27T07:15:46.470 回答