【问题标题】:SQL Find max no of consecutive months over a period of last 12 MonthsSQL 查找过去 12 个月内的最大连续月份数
【发布时间】:2022-01-22 12:10:40
【问题描述】:

我正在尝试在 sql 中编写一个查询,我需要在其中找到最大编号。过去 12 个月(不包括 6 月和 7 月)的连续月份。

所以例如我有一个如下的初始表

+---------+--------------+-----------+------------+
|      id | Payment      |  amount   |    Date    |
+---------+--------------+-----------+------------+
|       1 | CJ1          |     70000 | 11/3/2020  |
|       1 | 1B4          |  36314000 | 12/1/2020  |
|       1 | I21          | 119439000 | 1/12/2021  |
|       1 | 0QO          |   9362100 | 2/2/2021   |
|       1 | 1G0          | 140431000 | 2/23/2021  |
|       1 | 1G           |   9362100 | 3/2/2021   |
|       1 | g5d          |   9362100 | 4/6/2021   |
|       1 | rt5s         |  13182500 | 4/13/2021  |
|       1 | fgs5         |     48598 | 5/18/2021  |
|       1 | sd8          |     42155 | 5/25/2021  |
|       1 | wqe8         |  47822355 | 7/20/2021  |
|       1 | cbg8         |   4589721 | 7/27/2021  |
|       1 | jlk8         |   4589721 | 8/3/2021   |
|       1 | cxn9         |   4589721 | 10/5/2021  |
|       1 | qwe          |  45897210 | 11/9/2021  |
|       1 | mmm          |  45897210 | 12/16/2021 |
+---------+--------------+-----------+------------+

我写了下面的查询:

SELECT customer_number, year, month,
payment_month - lag(payment_month) OVER(partition by customer_number ORDER BY year, month) as previous_month_indicator,
FROM 
(
    SELECT DISTINCT Month(date) as month, Year(date) as year, CUSTOMER_NUMBER  
    FROM Table1
    WHERE Month(date) not in (6,7)
    and TO_DATE(date,'yyyy-MM-dd') >= DATE_SUB('2021-12-31', 425)
    and customer_number = 1
) As C

我得到了这个输出

+-----------------+------+-------+--------------------------+
| customer_number | year | month | previous_month_indicator |
+-----------------+------+-------+--------------------------+
|               1 | 2020 |    11 | null                     |
|               1 | 2020 |    12 | 1                        |
|               1 | 2021 |     1 | -11                      |
|               1 | 2021 |     2 | 1                        |
|               1 | 2021 |     3 | 1                        |
|               1 | 2021 |     4 | 1                        |
|               1 | 2021 |     5 | 1                        |
|               1 | 2021 |     8 | 3                        |
|               1 | 2021 |    10 | 2                        |
|               1 | 2021 |    11 | 1                        |
+-----------------+------+-------+--------------------------+

我想要的是得到这样的视图 预期输出


+-----------------+------+-------+--------------------------+
| customer_number | year | month | previous_month_indicator |
+-----------------+------+-------+--------------------------+
|               1 | 2020 |    11 |                        1 |
|               1 | 2020 |    12 |                        1 |
|               1 | 2021 |     1 |                        1 |
|               1 | 2021 |     2 |                        1 |
|               1 | 2021 |     3 |                        1 |
|               1 | 2021 |     4 |                        1 |
|               1 | 2021 |     5 |                        1 |
|               1 | 2021 |     8 |                        1 |
|               1 | 2021 |     9 |                        0 |
|               1 | 2021 |    10 |                        1 |
|               1 | 2021 |    11 |                        1 |
+-----------------+------+-------+--------------------------+

由于六月/七月无关紧要,五月之后,八月应该被认为是连续的月份,并且由于九月没有记录,所以它显示为0并且打破了连续的月份链。

我最终想要的输出是获得进行交易的最大连续月份数,在上述情况下是 8 从 2020 年 11 月到 2021 年 8 月

最终期望的输出:

+-----------------+-------------------------+
| customer_number | Max_consecutive_months |
+-----------------+-------------------------+
|               1 |                       8 |
+-----------------+-------------------------+

【问题讨论】:

  • 不清楚previous_month_indicator的填写规则是什么。假设第 8 个月有交易,那么最终输出的预期是什么。 Max_consecutive_months
  • @GeorgeJoseph,我想,我已经明确表示我要服用最后 12 个月,不包括 6 月/7 月,所以在我的情况下,第 8 个月(8 月)将是第 6 个月或发生在 5 月之后。所以,最终的飞蛾名单将是 Nov,Dec-2020,Jan-2021,Feb,Mar,Apr,Apr,May,Aug 等等......我希望给定数据 max_consecutive_months 将是 8 是有意义的

标签: sql gaps-and-islands


【解决方案1】:

CTE 可以更轻松地将其分解。在下面的代码中,payment_streak CTE 是关键位; start_of_streak 字段首先标记计为连胜开始的行,然后取所有先前行的最大值(以找到 this 连胜的开始)。

最后一个SELECT 只是比较这两个日期,计算它们之间的月数(不包括六月/七月),然后找到每个客户的最佳连胜记录。

WITH payments_in_context AS (
  SELECT customer_number,
    date,
    lag(date) OVER (PARTITION BY customer_number ORDER BY date) AS prev_date
  FROM Table1
  WHERE EXTRACT(month FROM date) NOT IN (6,7)
),
payment_streak AS (
  SELECT 
    customer_number,
    date,
    max(
    CASE WHEN (prev_date IS NULL)
           OR (EXTRACT(month FROM date) <> 8
                 AND (date - prev_date >= 62 
                    OR MOD(12 + EXTRACT(month FROM date) - EXTRACT(month FROM prev_date),12)) > 1))
           OR (EXTRACT(month FROM date) = 8
                 AND (date - prev_date >= 123
                    OR EXTRACT(month FROM prev_date) NOT IN (5,8)))
         THEN date END
    ) OVER (PARTITION BY customer_number ORDER BY date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW)
    as start_of_streak
    FROM payments_in_context
)
SELECT customer_number,
  max( 1 +
    10*(EXTRACT(year FROM date) - EXTRACT(year FROM start_of_streak))
      + (EXTRACT(month FROM date) - EXTRACT(month FROM start_of_streak))
      + CASE WHEN (EXTRACT(month FROM date) > 7 AND EXTRACT(month FROM start_of_streak) < 6)
             THEN -2
             WHEN (EXTRACT(month FROM date) < 6 AND EXTRACT(month FROM start_of_streak) > 7)
             THEN 2
             ELSE 0 END
     ) AS max_consecutive_months
FROM payment_streak
GROUP BY 1;

【讨论】:

  • 我试过你的代码,它几乎适用于我拥有的所有数据,但在一种情况下,条纹在 5 月结束,它不会返回正确的编号。连续几个月。你能帮忙吗?
  • 我想为这个特殊案例捕获的主要连胜开始于2021-12-04,结束于2021-05-28,我为这个案例得到的最后 max_consecutive_months 是 4,应该是 6。任何建议如何我能解决吗?
  • 哦,是的,我的错,它是 2020-12-04 并以 2021-05-28 结尾
  • 但是如果没有Aug,则主要问题存在于最后一个select,如果没有Aug,则在May处停止,最大值在3处。
  • 谢谢,我在最后一个案例陈述中遗漏了一个案例。整年的表现就像连续 10 个月。开始在夏隔之前,结束在夏隔之后的情况,需要减2,相反的情况,需要加2。
【解决方案2】:

您可以使用递归cte 为每个客户id 生成十二个月时间跨度内的所有日期,然后找到该时间间隔内不包括六月和七月的最大连续日期数:

with recursive cte(id, m, c) as (
   select cust_id, min(date), 1 from payments group by cust_id
   union all
   select c.id, c.m + interval 1 month, c.c+1 from cte c where c.c <= 12
),
dts(id, m, f) as (
   select c.id, c.m, c.c = 1 or exists 
       (select 1 from payments p where p.cust_id = c.id and extract(month from p.date) = extract(month from (c.m - interval 1 month)) and extract(year from p.date) = extract(year from (c.m - interval 1 month))) 
   from cte c where extract(month from c.m) not in (6,7)
),
result(id, f, c) as (
  select d.id, d.f, (select sum(d.id = d1.id and d1.m < d.m and d1.f = 0)+1 from dts d1) 
  from dts d where d.f != 0
)
select r1.id, max(r1.s)-1 from (select r.id, r.c, sum(r.f) s from result r group by r.id, r.c) r1 group by r1.id

【讨论】:

  • 对不起,前面没有提到,但我使用的是 spark sql,并且没有递归 cte,你知道有什么解决方法吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-04-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-23
  • 2010-10-11
相关资源
最近更新 更多