我在PosgreSQL数据库中有一个表,其中包含有关已激活订阅的数据。该表具有以下列:用户ID、订阅的开始日期、订阅的结束日期和订阅的类型。
如果订阅当前处于活动状态,则订阅结束日期设置为今天的日期。
有两种独立的订阅类型:1.高级,2.书籍。它们是独立激活的。例如,数据可能如下所示:
| 用户ID|认购开始日期|订阅结束日期|订阅类型|
| --|--|--|--|
| 675 |2023-01-01 2023-01-01| 2023-05-10 2023-05-10 2023-05-10|溢价|
| 675 |2023-02-15 2023-02-15 2023-02-15| 2023-02-28 2023-02-28 2023-02-28|书|
| 675 |2023-04-18 2023-04-18 2023-04-18| 2023-06-18 2023-06-18 2023-06-18|书|
| 726 |2023-01-01 2023-01-01| 2023-10-10 2023-10-10|溢价|
| 726 |2023-03-16 2023-03-16 2023-03-16| 2023-05-28 2023-05-28 2023-05-28|书|
| 855 |2023-04-05 2023-04-05 2023-04-05| 2023-05-28 2023-05-28 2023-05-28|书|
| 855 |2023-04-20 2023-04-20 2023-04-20| 2023-07-25 2023-07-25 2023-07-25|溢价|
我需要的是,如果另一个订阅(图书)在其有效期内被激活,则调整Premium订阅的期限。换句话说,Premium订阅应在图书订阅激活之前和之后分为几个时期。所需的输出如下所示:
| 用户ID|认购开始日期|订阅结束日期|订阅类型|
| --|--|--|--|
| 675 |2023-01-01 2023-01-01| 2023-02-15 2023-02-15 2023-02-15|溢价|
| 675 |2023-02-15 2023-02-15 2023-02-15| 2023-02-28 2023-02-28 2023-02-28|书|
| 675 |2023-02-28 2023-02-28 2023-02-28| 2023-04-18 2023-04-18 2023-04-18|溢价|
| 675 |2023-04-18 2023-04-18 2023-04-18| 2023-06-18 2023-06-18 2023-06-18|书|
| 726 |2023-01-01 2023-01-01| 2023-03-16 2023-03-16 2023-03-16|溢价|
| 726 |2023-03-16 2023-03-16 2023-03-16| 2023-05-28 2023-05-28 2023-05-28|书|
| 726 |2023-05-28 2023-05-28 2023-05-28| 2023-10-10 2023-10-10|溢价|
| 855 |2023-04-05 2023-04-05 2023-04-05| 2023-05-28 2023-05-28 2023-05-28|书|
| 855 |2023-05-28 2023-05-28 2023-05-28| 2023-07-25 2023-07-25 2023-07-25|溢价|
这样做的smth,但它分裂的第一个订阅名为保费到下一个子“书”之前的时期.但我不明白如何继续子“保费”后“书”完成.
WITH ordered_subscriptions AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY user_id ORDER BY start_date) as rn
FROM subscriptions
),
date_ranges AS (
SELECT
a.user_id,
a.start_date,
MIN(b.start_date) as end_date,
a.subscription_type
FROM ordered_subscriptions a
LEFT JOIN ordered_subscriptions b ON a.user_id = b.user_id AND a.rn < b.rn
GROUP BY a.user_id, a.start_date, a.subscription_type
),
filtered_subscriptions AS (
SELECT
user_id,
start_date,
COALESCE(end_date, CURRENT_DATE) as end_date,
subscription_type
FROM date_ranges
WHERE subscription_type = 'Premium' AND NOT EXISTS (
SELECT 1
FROM date_ranges d2
WHERE d2.user_id = date_ranges.user_id
AND d2.subscription_type = 'Books'
AND d2.start_date < date_ranges.end_date
AND (d2.end_date IS NULL OR d2.end_date > date_ranges.start_date)
)
)
SELECT * FROM filtered_subscriptions
UNION ALL
SELECT user_id, start_date, COALESCE(end_date, CURRENT_DATE), subscription_type
FROM date_ranges
WHERE subscription_type = 'Books'
ORDER BY user_id, start_date;
字符串
2条答案
按热度按时间i34xakig1#
假设您希望保留所有的 Books 订阅。如果有一个 Premium 订阅以某种方式重叠,则重叠的期间需要被吸收到另一个订阅中,同时调整/创建非重叠的(可能是两个)。
两个这样的订阅可以有四种不同的交叉方式。还有一种可能性是,多个 Books 可以归入一个 Premium。查询的第一部分确定是否存在重叠,重叠的类型,并收集与这种链中的前向和后向链接相关的数据。
第二部分生成一组四个日期,对应于重叠的不同部分之间的边界。它还处理创建一个额外的行,其中 Premium 必须分为三部分(一部分保留为 Books),并且 Premium 完全包含在 Books 中,并且需要完全消失。
其余的逻辑只是驱动所有部件的组装。注意不要在“链接”发生的地方复制片段。
这里没有递归,只有一个连接,所以我认为这将是有效的。在列
user_id, start_date, end_date
上建立适当的索引可能会有好处。字符串
https://dbfiddle.uk/chT-SLiC
我原本认为使用外部连接会比使用联合更干净,生成更好的计划。潜在地,即使两个查询大部分相同,实际上更好地走另一条路。
https://dbfiddle.uk/raMy-wOL的
**UPDATE:**我更喜欢这个版本的查询。这是一个稍微不同的方法,它可以处理一些其他方法不能处理的情况。我会尝试将这些合并在一起,但现在我会忽略上面的查询。
型
https://dbfiddle.uk/Y2T_q-kV的
k10s72fa2#
请参阅示例。
subscription_type为
Premium
的期间与行Books
(更准确地说,不是Premium)联接在一起。字符串
为了减少计算量,我们还立即取上一个
Books
周期的结束和下一个周期的开始。“如果帐簿期间与保险费期间完全重叠,则条件1和2将不满足,该行将从输出中排除(内部联接)。
对于联合序列,如2023-12-01->2023 - 12-04 2023 <->-12-04->2023-12-16,需要澄清条件。
型
Fiddle here