【问题标题】:Gaps and Islands ? Or Not?差距和岛屿?或不?
【发布时间】:2015-10-29 15:48:51
【问题描述】:

我有下表:

;with data as (
select '508325'  as [customer], 'G61' as [demo_given],
        cast('2015-1-1' as date) as [date_of_demo]
union all select '508325', 'G61', cast('2015-3-1' as date) 
union all select '508325', 'G61',cast('2015-3-15' as date)
union all select '508325', 'G61',cast('2015-3-16' as date)
union all select '508325', 'G61',cast('2015-3-17' as date)
union all select '508325', 'G61',cast('2015-6-1' as date)
union all select '508325', 'G61',cast('2015-8-1' as date)
union all select '508325', 'G61',cast('2015-9-1' as date)
union all select '508325', 'G61',cast('2015-9-1' as date)
union all select '508325', 'G61',cast('2015-12-1' as date)
)

每个客户在 4 个月内只能提供 3 个演示。 第一个时期从给出的第一个演示开始计算,并在 4 个月后结束。

如果该期间的演示数量超过 3,我需要演示的日期 4 以及该 4 个月期间的后期。 (在本例中为2015-3-162015-3-17

下一个时期从前四个月后的第一个演示日期开始。所以我需要计算2015-6-12015-9-30 期间的演示数量,并返回该期间给出的最终“剩余”演示的日期。

我该怎么做呢?

【问题讨论】:

    标签: sql-server tsql sql-server-2012 gaps-and-islands


    【解决方案1】:

    为了便于阅读,我使用了多步 CTE,但您可以根据需要将其组合起来:

    • tally - 简单的数字表,您可以使用任何您想要的方法(递归 cte、虚拟表、表函数......)
    • min_date_per_customer - 获取每位客户的首次演示日期
    • date_ranges - 生成将 4 个月添加到 min_date 的范围
    • final - 将data 加入date_ranges,生成行号
    • main query - 过滤掉特定时间段内第 4、5、6、...的演示

    代码:

    WITH tally(N) AS (
      SELECT ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
      FROM sys.all_columns a CROSS JOIN sys.all_columns b
    ), min_date_per_customer AS (
      SELECT customer, MIN(date_of_demo) AS min_date
      FROM #data
      GROUP BY customer
    ), date_ranges AS (
      SELECT t.N, mdpc.customer
          ,[date_start] = DATEADD(m, 4 * (t.N - 1), min_date)
          ,[date_end]   = DATEADD(m, 4 *t.N, min_date)
      FROM min_date_per_customer mdpc
      CROSS JOIN tally t
      WHERE t.N < 100 -- you can generate as many period as you wish
    ), final AS (
      SELECT d.customer
           ,d.demo_given
           ,d.date_of_demo
           ,dr.N
           ,rn = ROW_NUMBER() OVER (PARTITION BY dr.customer, dr.N ORDER BY date_of_demo)
      FROM #data d
      JOIN date_ranges dr
        ON d.[date_of_demo] >= dr.date_start
       AND d.[date_of_demo] <= dr.date_end
       AND d.customer = dr.customer
    )
    SELECT *
    FROM final
    WHERE rn > 3
    ORDER BY customer, date_of_demo;
    

    LiveDemo

    输出:

    ╔══════════╦════════════╦═════════════════════╦═══╦═════╗
    ║ customer ║ demo_given ║    date_of_demo     ║ N ║ rn  ║
    ╠══════════╬════════════╬═════════════════════╬═══╬═════╣
    ║   508325 ║ G61        ║ 2015-03-16 00:00:00 ║ 1 ║   4 ║
    ║   508325 ║ G61        ║ 2015-03-17 00:00:00 ║ 1 ║   5 ║
    ║   508325 ║ G61        ║ 2015-09-01 00:00:00 ║ 2 ║   4 ║
    ╚══════════╩════════════╩═════════════════════╩═══╩═════╝
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多