Python 如何在SQLAlchemy或SQL中查找与时间间隔交集中的月份匹配的条目?

Python 如何在SQLAlchemy或SQL中查找与时间间隔交集中的月份匹配的条目?,python,sql,postgresql,sqlalchemy,Python,Sql,Postgresql,Sqlalchemy,我的一个用户想回答以下问题: “2005年至2010年3月期间存在哪些请求?” DB表“request”有两列,start\u date和end\u date,表示请求生命周期的间隔 SQLAlchemy模型如下所示: class Request(SomeBaseModel): ... start_date = db.Column(db.Date, default=date.today) end_date = db.Column(db.Date, default=in_o

我的一个用户想回答以下问题:

“2005年至2010年3月期间存在哪些请求?”

DB表“request”有两列,
start\u date
end\u date
,表示请求生命周期的间隔

SQLAlchemy模型如下所示:

class Request(SomeBaseModel):
    ...
    start_date = db.Column(db.Date, default=date.today)
    end_date = db.Column(db.Date, default=in_one_year)
然后我有一个进程,在Python中动态地获得5年零一个月的跨度:

`initial_date`, five_years_later, month_number = getTimePeriod()
根据这些参数,我必须列出在
初始日期
五年之后
之间开始或结束的所有请求。通过将
开始日期
结束日期
初始日期
五年后
进行比较,我可以很容易地做到这一点

然而,困难的部分是仅获取在该特定月份存在的请求,而该月份也是(
初始日期
五年后
)间隔的一部分。这些规则是:

  • 请求可以在本月之前和之后存在,但该月必须在其生命周期内
  • 月份可以在请求生命周期中出现多次,但不能出现0次
  • 同一个月必须出现在一个请求生命周期内,并且出现在(
    初始日期
    五年后
    )间隔内
我可以通过为(
initial\u date
five\u years\u later
)间隔的每一年生成每个月的开始日期和结束日期,然后检查这些对是否与请求生命周期重叠:

        filters = []
        for year in range(initial_date, five_years_later + 1):
            month_start_date = datetime(year, month, 1)
            month_end_date = datetime(year, month, calendar.mdays[month])
            filters.append(
                (requeest.start_date <= month_end_date) &
                (request.end_date >= month_start_date)
            )
        is_active = functools.reduce(operator.or_, filters)
        auth_requests = auth_requests.filter(is_active)
过滤器=[]
对于范围内的年份(初始日期,五年后+1):
月\开始\日期=日期时间(年、月、1)
月份\结束\日期=日期时间(年、月、日历.mdays[月])
filters.append(
(重新测试开始日期=月份开始日期)
)
是否激活=functools.reduce(运算符或过滤器)
auth\u requests=auth\u requests.filter(是否处于活动状态)
然而,我的直觉告诉我还有更好的办法


SQLAlchemy查询可能是最好的答案,但Postgres的SQL版本也可以

实际上,您在问题中的提案构造了一组过滤器,用于评估开始日期和结束日期与所讨论月份之间的交集。因此,将月份、开始日期和结束日期转换为大约五个过滤器(取决于边界条件),然后使用or运算符。 假设您的列被正确地索引(或者如果数据没有被索引,则完整的表扫描是正确的答案),我认为没有什么比这更好的了。您的查询为Postgres提供了一组要比较的时间间隔。每行最多需要处理一次。 对于这个问题,这是理想的。
所以我的答案是,你们已经找到了一个最好的方法。可能还有其他方法具有相同的性能特征,但您所拥有的方法很容易理解,并且性能很好。

以下是我如何分解问题的

或者 -请求的持续时间应大于等于1年

或 -开始日期必须在三月期间或之后,结束日期应在三月期间或之前

开始日期/结束日期必须在范围内(20052010)

此postgres查询将检查以下条件:

select * from request
where 1 = 
CASE
    WHEN extract(year from age(end_date,start_date)) >= 1 THEN 1
    WHEN (extract(month from start_date)::integer <= 3 
             AND extract(month from end_date)::integer >= 3 )
         AND extract(year from age(end_date,start_date)) < 1 THEN 1
    WHEN (extract(month from start_date)::integer >= 3 
             AND extract(month from end_date)::integer <= 3 )
         AND extract(year from age(end_date,start_date)) < 1 THEN 1
    ELSE 0
END
AND ((extract(year from start_date)::integer >= 2005
and  extract(year from start_date)::integer <= 2010)
OR (extract(year from end_date)::integer >= 2005
and  extract(year from end_date)::integer <= 2010))
;
从请求中选择*
其中1=
案例
当提取(从年龄开始的年份(结束日期、开始日期))>=1时,则为1
时间(提取(从开始日期算起的月份)::整数=3)
并提取(从年龄开始的年份(结束日期、开始日期))<1然后1
何时(提取(从开始日期算起的月份)::整数>=3
和提取(从结束日期算起的月份)::整数=2005
和提取(从开始日期算起的年份)::整数=2005

和extract(从结束日期算起的年份)::似乎比我自己的解决方案复杂得多的整数。至少相信这一点感觉很好。到目前为止,查询执行得很好,尽管我对索引不太了解,不知道我应该在这一点上索引什么。我猜是什么日期?