我有一项服务,顾客可以开“客服票”,是收费的。无论何时,他们都可以在接下来的30天内再开多少张“客户服务票”,而无需付费;在此期限之后,如果他们再次开票,他们将再次被收取费用(如果发生这种情况,他们将获得另外 30 天的免费客户服务)。
基于此,我有一个名为 customer_service_ticket 的表,其中我将每张票证及其日期存储在“日期”列中。
什么查询获取所有收费门票,即这些打开 30 天窗口的门票(而不是其中的门票)?
例如。如果门票有日期的话
3/一月 - 4/一月 - 8/一月 - 30/一月 - 4/三月 - 20/三月 - 20/三月
查询应该只返回
1 月 3 日 - 3 月 4 日 - 3 月 20 日。
因为每个语句都应用于所有行,所以我找不到解决方案。我正在研究递归 CTE。
此类问题需要递归 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
。
你说得完全正确;对于这种特定场景,使用递归 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 天期限的开始,在此期间客户可以开立额外的“客户服务票证”而无需付费。