postgresql 基于先前行的查询值:打开30天窗口的值(不在其中)

uurv41yg  于 2024-01-07  发布在  PostgreSQL
关注(0)|答案(2)|浏览(159)

我有一项服务,客户可以打开一个“客户服务票”,这是收费的。每当他们这样做,他们可以打开多少“客户服务票”,他们想在未来30天内没有被收费;在此期间之后,如果他们再次打开一个票,他们将再次收费(如果发生这种情况,他们将有另一个30天的免费客户服务)。
在此基础上,我有一个名为customer_service_ticket的表,在其中存储每张票据,并将其日期存储在“date”列中。
什么查询获取所有可收费的票,也就是说,这些票打开30天的窗口(而不是其中的票)?
如果票上有日期
3/1月4日/1月8日/1月30日/1月4日/20日/20日/3月
查询应该只返回
1月3日至2月4日至3月20日
因为每个语句都应用于所有行,所以我找不到解决方案。我正在使用递归CTE。

krcsximq

krcsximq1#

这种类型的问题需要递归CTE,因为选定的行依赖于先前选定的行。
下面演示了执行请求的操作的查询:

WITH RECURSIVE
  tickets(ticket_id, customer_id, ticket_date) AS (
    VALUES (1, 1, '2023-01-03'::date),
           (2, 1, '2023-01-04'::date),
           (3, 1, '2023-01-30'::date),
           (4, 1, '2023-02-04'::date),
           (5, 1, '2023-02-20'::date),
           (6, 1, '2023-03-20'::date)
  ),
  parms AS (
    SELECT 30 AS support_days
  ),
  charged_tickets AS (
    SELECT * FROM (SELECT DISTINCT ON (t.customer_id) t.customer_id,
                          t.ticket_id,
                          t.ticket_date,
                          t.ticket_date + p.support_days AS next_chargeable_start
                     FROM parms p
                     CROSS JOIN tickets t
                     ORDER BY t.customer_id, t.ticket_date, t.ticket_id) first_charged_tickets
    UNION ALL
    SELECT * FROM (SELECT DISTINCT ON (t.customer_id) t.customer_id,
                          t.ticket_id,
                          t.ticket_date,
                          t.ticket_date + p.support_days AS next_chargeable_start
                     FROM parms p
                     CROSS JOIN tickets t
                     JOIN charged_tickets
                       ON t.customer_id = charged_tickets.customer_id
                         AND t.ticket_date >= charged_tickets.next_chargeable_start
                     ORDER BY t.customer_id, t.ticket_date, t.ticket_id) next_charged_tickets
  )
SELECT ct.customer_id, ct.ticket_id, ct.ticket_date
  FROM charged_tickets ct
  ORDER BY ct.customer_id, ct.ticket_date;

字符串
结果如下:
| 客户标识|机票ID|出票日期|
| --|--|--|
| 1 | 1 |2023-01-03 2023-01-03|
| 1 | 4 |2023-02-04 2023-02-04|
| 1 | 6 |2023-03- 20 2023-03-20 2023-03-20|
查询首先将示例数据建立为tickets(这将是一个用于生产的实际表)。支持天数在parms中定义。这允许查询更DRY(Don 'tRepeatYourself)。
递归CTE在charged_tickets中找到。初始化部分确定每个客户的第一张收费票据和下一次收费的日期。迭代部分对每个连续的收费票据执行相同的操作。charged_tickets中的子查询是必要的,因为PostgreSQL在递归子查询中不直接支持DISTINCT ON

nukf8bse

nukf8bse2#

你说得太对了使用递归CTE对于这个特定的场景来说可能有点过于复杂。2相反,窗口函数和子查询的组合可以有效地实现所需的结果。3该方法包括识别每个30天窗口的开始日期,并仅选择那些票据。
假设您的表名为customer_service_ticket,并且包含ticket_id和date等列,则以下查询应提供所需的输出:

WITH TicketWithPreviousDate AS (
    SELECT
        date,
        LAG(date) OVER (ORDER BY date) AS previous_date
    FROM
        customer_service_ticket
)

SELECT
    date
FROM
    TicketWithPreviousDate
WHERE
    previous_date IS NULL OR
    date >= DATE_ADD(previous_date, INTERVAL 30 DAY)
ORDER BY
    date;

字符串
此查询返回客户服务票证的日期,这些票证标志着新的30天期间的开始,在此期间,客户可以打开其他“客户服务票证”而不收取费用。

相关问题