差距和岛屿?或不?
Gaps and Islands ? Or Not?
我有以下 table:
;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-16
和 2015-3-17
)
下一期从前四个月后第一次演示的日期开始。因此,我需要计算 2015-6-1
期间的演示数量,直到 2015-9-30
和 return 期间最终 'surplus' 演示的日期。
我该怎么做?
为了便于阅读,我使用了多步 CTE,但如果需要,您可以将其组合:
tally
- 简单数字 table 你可以使用任何你想要的方法(递归 cte,虚拟 table,table 函数,...)
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;
输出:
╔══════════╦════════════╦═════════════════════╦═══╦═════╗
║ 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 ║
╚══════════╩════════════╩═════════════════════╩═══╩═════╝
我有以下 table:
;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-16
和 2015-3-17
)
下一期从前四个月后第一次演示的日期开始。因此,我需要计算 2015-6-1
期间的演示数量,直到 2015-9-30
和 return 期间最终 'surplus' 演示的日期。
我该怎么做?
为了便于阅读,我使用了多步 CTE,但如果需要,您可以将其组合:
tally
- 简单数字 table 你可以使用任何你想要的方法(递归 cte,虚拟 table,table 函数,...)min_date_per_customer
- 获取每个客户的首次演示日期date_ranges
- 生成范围将 4 个月添加到 min_datefinal
- 加入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;
输出:
╔══════════╦════════════╦═════════════════════╦═══╦═════╗
║ 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 ║
╚══════════╩════════════╩═════════════════════╩═══╩═════╝