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

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

我有一项服务,客户可以打开一个“客户服务票”,这是收费的。每当他们这样做,他们可以打开多少“客户服务票”,他们想在未来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,因为选定的行依赖于先前选定的行。
下面演示了执行请求的操作的查询:

  1. WITH RECURSIVE
  2. tickets(ticket_id, customer_id, ticket_date) AS (
  3. VALUES (1, 1, '2023-01-03'::date),
  4. (2, 1, '2023-01-04'::date),
  5. (3, 1, '2023-01-30'::date),
  6. (4, 1, '2023-02-04'::date),
  7. (5, 1, '2023-02-20'::date),
  8. (6, 1, '2023-03-20'::date)
  9. ),
  10. parms AS (
  11. SELECT 30 AS support_days
  12. ),
  13. charged_tickets AS (
  14. SELECT * FROM (SELECT DISTINCT ON (t.customer_id) t.customer_id,
  15. t.ticket_id,
  16. t.ticket_date,
  17. t.ticket_date + p.support_days AS next_chargeable_start
  18. FROM parms p
  19. CROSS JOIN tickets t
  20. ORDER BY t.customer_id, t.ticket_date, t.ticket_id) first_charged_tickets
  21. UNION ALL
  22. SELECT * FROM (SELECT DISTINCT ON (t.customer_id) t.customer_id,
  23. t.ticket_id,
  24. t.ticket_date,
  25. t.ticket_date + p.support_days AS next_chargeable_start
  26. FROM parms p
  27. CROSS JOIN tickets t
  28. JOIN charged_tickets
  29. ON t.customer_id = charged_tickets.customer_id
  30. AND t.ticket_date >= charged_tickets.next_chargeable_start
  31. ORDER BY t.customer_id, t.ticket_date, t.ticket_id) next_charged_tickets
  32. )
  33. SELECT ct.customer_id, ct.ticket_id, ct.ticket_date
  34. FROM charged_tickets ct
  35. 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等列,则以下查询应提供所需的输出:

  1. WITH TicketWithPreviousDate AS (
  2. SELECT
  3. date,
  4. LAG(date) OVER (ORDER BY date) AS previous_date
  5. FROM
  6. customer_service_ticket
  7. )
  8. SELECT
  9. date
  10. FROM
  11. TicketWithPreviousDate
  12. WHERE
  13. previous_date IS NULL OR
  14. date >= DATE_ADD(previous_date, INTERVAL 30 DAY)
  15. ORDER BY
  16. date;

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

展开查看全部

相关问题