从 Postgres 中的非结束日期范围列表中查找未覆盖的日期范围

Posted

技术标签:

【中文标题】从 Postgres 中的非结束日期范围列表中查找未覆盖的日期范围【英文标题】:Finding non-overridden date range from a list of non-ending date ranges in Postgres 【发布时间】:2021-01-07 14:39:10 【问题描述】:

我有一个表格来存储从给定日期开始的sku 的价格:start_date。对于sku 的新价格,此表中可能有多个条目,新的start_date。从添加新价格之日起,每个新条目都会覆盖上一组行中的价格。

表结构是这样的:

sku_id | start_date   | price

100    | "2020-01-10" | 100
100    | "2020-01-20" | 200
100    | "2020-01-30" | 300

有了这些条目,10th Jan21st Jan31st Jan 的价格将分别显示为 100200300

现在,如果我们在此表中输入另一个条目,开始日期为1st Jan,价格为500。然后,直到覆盖所有这 3 个价格。然后,之前获取的所有 3 个日期的价格现在将变为 500

sku_id | start_date   | price

100    | "2020-01-10" | 100
100    | "2020-01-20" | 200
100    | "2020-01-30" | 300
100    | "2020-01-01" | 500    -> This row overrides prices of all 3 rows before it. Since start date `2020-01-01` is less then all their start date.

鉴于这个表结构,这是我的要求:

我想获取所有仍处于活动状态的记录(这意味着它们不会被任何新创建的行完全覆盖)。我想到了使用 LEAD 函数,它可以让我得到每一行的 end_date。

SELECT sku_id, start_date, price,
    LEAD(start_date, 1) OVER (
        PARTITION BY sku_id ORDER BY created_at
    ) - INTERVAL '1 day' AS end_date
FROM rate;

这个查询给了我这个结果:

sku_id | start_date   | price  | end_date

100    | "2020-01-10" | 100    | "2020-01-19"
100    | "2020-01-20" | 200    | "2020-01-29"
100    | "2020-01-30" | 300    | "2019-12-31"
100    | "2020-01-01" | 500    | 

在此之后,我需要一些查询,它可以拒绝第 1、2 和 3 行,因为它们的开始日期小于第 3 行的结束日期。这是我无法理解如何实现的。

如果问题的标题没有意义,真的很抱歉,因为我想不出要给出什么问题的标题。

我采用的另一种方法是按降序保持加载记录。然后使用该记录作为游标,获取 start_date 小于该记录开始日期的先前行。但这会导致大量往返数据库,这是我想避免的。

如果有一个解决方案,只需一个 SQL 查询即可获得我正在寻找的内容,那就太好了。

【问题讨论】:

您打算在桌子上放多少行。可以完成查询,但根据表的大小,可能会出现严重的性能问题。如果性能是您所需要的,那么您必须使用另一种方法来解决这个问题,而不仅仅是窗口查询。 每个 sku,这个表不会有那么多行。您可以说,每个 sku 将少于 1000 行。 我很难相信前三行的start_dates 小于第三行的end_date,因为我很确定 2020 年 > 2019 年。这整件事充其量是可疑的。 @AdrianKlaver 我认为您没有完全阅读这个问题。坦率地说,我无法更好地解释它。当您假设有可能为未来日期设置价格时,这一切都是有道理的,这发生在酒店房间预订软件中。 【参考方案1】:

以下查询可以提供所需的输出。

select t.sku_id, t.start_date, t.price, t.created_at from
(select rate.*, min(start_date) over (partition by sku_id order by created_at desc) as calculated_date
from rate) t
where t.calculated_date = t.start_date

说明:

使用windows函数,为当前记录之后创建的条目确定min start date。 如果开始日期大于计算出的min start date,则过滤掉记录。

参考:https://www.db-fiddle.com/f/uZgWjur4cmqwUjuLPcCNP5/1

【讨论】:

以上是关于从 Postgres 中的非结束日期范围列表中查找未覆盖的日期范围的主要内容,如果未能解决你的问题,请参考以下文章

从表中的开始日期和结束日期在 Postgres 中生成系列

Mysql 跨行查找日期范围

如何从 Swift 中特定工作日的日期范围中查找日期?

上个月开始和结束的 Postgres 纪元摘录

错误:Postgresql 中的日期/时间字段值超出范围

在包含多个开始和日期列表的表中查找日期,如果找到则返回默认值