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

问题描述 投票:0回答:2

我有一项服务,顾客可以开“客服票”,是收费的。无论何时,他们都可以在接下来的30天内再开多少张“客户服务票”,而无需付费;在此期限之后,如果他们再次开票,他们将再次被收取费用(如果发生这种情况,他们将获得另外 30 天的免费客户服务)。

基于此,我有一个名为 customer_service_ticket 的表,其中我将每张票证及其日期存储在“日期”列中。

什么查询获取所有收费门票,即这些打开 30 天窗口的门票(而不是其中的门票)?

例如。如果门票有日期的话
3/一月 - 4/一月 - 8/一月 - 30/一月 - 4/三月 - 20/三月 - 20/三月

查询应该只返回
1 月 3 日 - 3 月 4 日 - 3 月 20 日。

因为每个语句都应用于所有行,所以我找不到解决方案。我正在研究递归 CTE。

sql postgresql date relational-database
2个回答
0
投票

此类问题需要递归 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 ticket_id 门票_日期
1 1 2023-01-03
1 4 2023-02-04
1 6 2023-03-20

查询首先将示例数据建立为

tickets
(这将是用于生产用途的实际表)。支持天数在
parms
中定义。这使得查询更加干燥(Don't R重复Y我们自己)。

递归 CTE 位于

charged_tickets
。初始化部分确定每个客户的第一张收费票据以及下次再次收费的日期。迭代部分对每张连续的收费票据执行相同的操作。
charged_tickets
中的子查询是必需的,因为 PostgreSQL 不直接在递归子查询中支持
DISTINCT ON


-1
投票

你说得完全正确;对于这种特定场景,使用递归 CTE 可能有点过于复杂。相反,窗口函数和子查询的组合可以有效地实现所需的结果。该方法涉及确定每个 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 天期限的开始,在此期间客户可以开立额外的“客户服务票证”而无需付费。

© www.soinside.com 2019 - 2024. All rights reserved.