我正在尝试按月计算客户流失率。我有一个表customer_key,12个月标志作为列。我需要一个结果输出表来显示:月份编号(1-12),分组字段(任期带:< 1年,1-3岁,3-5岁,5岁以上)和流失计数。例如:
月任期流失
第1个月< 1yr 1,234;
第2个月< 1yr 656;
...
第12个月< 1yr 777;
流失计数的计算方法是减去一个月内“存在”的客户数量减去下个月“存在”的数量,由Mon1_Basic_Flag和Mon2_Basic_Flag表示。目前,我使用以下代码来获得此结果:
SELECT
'M01' AS Monthnumber
,case when DATEDIFF(month,inception_dt,@fmonth)<12 then '<1yr'
when DATEDIFF(month,inception_dt,@fmonth) between 12 and 36 then '1-3yr'
when DATEDIFF(month,inception_dt,@fmonth) between 36 and 60 then '3-5yr'
when DATEDIFF(month,inception_dt,@fmonth)>60 then '>5yr' end as tenureband
,SUM(CASE WHEN MON1_BASIC_FLAG >0 THEN 1 ELSE 0 END) -SUM(CASE WHEN MON2_BASIC_FLAG>0 then 1 else 0 end
as churn
from dbo.customers
group by inception_dt
union all
SELECT
'M02' AS Monthnumber
,case when DATEDIFF(month,inception_dt,@fmonth)<12 then '<1yr'
when DATEDIFF(month,inception_dt,@fmonth) between 12 and 36 then '1-3yr'
when DATEDIFF(month,inception_dt,@fmonth) between 36 and 60 then '3-5yr'
when DATEDIFF(month,inception_dt,@fmonth)>60 then '>5yr' end as tenureband
,SUM(CASE WHEN MON2_BASIC_FLAG >0 THEN 1 ELSE 0 END) -SUM(CASE WHEN MON3_BASIC_FLAG>0 then 1 else 0 end
as churn
from dbo.customers
group by inception_dt
union all
....
union all
SELECT
'M11' AS Monthnumber
,case when DATEDIFF(month,inception_dt,@fmonth)<12 then '<1yr'
when DATEDIFF(month,inception_dt,@fmonth) between 12 and 36 then '1-3yr'
when DATEDIFF(month,inception_dt,@fmonth) between 36 and 60 then '3-5yr'
when DATEDIFF(month,inception_dt,@fmonth)>60 then '>5yr' end as tenureband
,SUM(CASE WHEN MON11_BASIC_FLAG >0 THEN 1 ELSE 0 END) -SUM(CASE WHEN MON12_BASIC_FLAG>0 then 1 else 0 end
as churn
from dbo.customers
group by inception_dt
然而,重复12次,此代码在进行任何更改时会留下很大的错误空间。我想把它放在一个循环中,以便它每个月重复相同的计算。我可以很容易地在SAS中做到这一点,但我已经到处寻找这个概念的SQL翻译。有什么建议? 谢谢!
答案 0 :(得分:2)
你可以create a function做到这一点。
CREATE TABLE Customers
(
Inception datetime,
MON1_BASIC_FLAG int, MON2_BASIC_FLAG int, MON3_BASIC_FLAG int, MON4_BASIC_FLAG int,
MON5_BASIC_FLAG int, MON6_BASIC_FLAG int, MON7_BASIC_FLAG int, MON8_BASIC_FLAG int,
MON9_BASIC_FLAG int, MON10_BASIC_FLAG int, MON11_BASIC_FLAG int, MON12_BASIC_FLAG int
)
INSERT INTO Customers VALUES ('2010-01-01', 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
GO
CREATE FUNCTION TenureBand(@startDate datetime, @endDate datetime)
RETURNS varchar(5)
BEGIN
DECLARE @diff int
SELECT @diff = DATEDIFF(month, @startDate, @endDate)
RETURN CASE
WHEN @diff < 12 then '<1yr'
WHEN @diff BETWEEN 12 AND 36 THEN '1-3yr'
WHEN @diff BETWEEN 36 AND 60 THEN '3-5yr'
ELSE '>5yr'
END
END
GO
SELECT Inception,
[01] - [02] as [M01], [02] - [03] as [M02], [03] - [04] as [M03],
[04] - [05] as [M04], [05] - [06] as [M05], [06] - [07] as [M06],
[07] - [08] as [M07], [08] - [09] as [M08], [09] - [10] as [M09],
[10] - [11] as [M10], [11] - [12] as [M11]
INTO #TempTable
FROM (
SELECT Inception,
SUM(MON1_BASIC_FLAG) as [01], SUM(MON2_BASIC_FLAG) as [02],
SUM(MON3_BASIC_FLAG) as [03], SUM(MON4_BASIC_FLAG) as [04],
SUM(MON5_BASIC_FLAG) as [05], SUM(MON6_BASIC_FLAG) as [06],
SUM(MON7_BASIC_FLAG) as [07], SUM(MON8_BASIC_FLAG) as [08],
SUM(MON9_BASIC_FLAG) as [09], SUM(MON10_BASIC_FLAG) as [10],
SUM(MON11_BASIC_FLAG) as [11], SUM(MON12_BASIC_FLAG) as [12]
FROM Customers
GROUP BY Inception
) p
SELECT Inception,
[#TempTable] as [MonthNumber],
dbo.TenureBand(Inception, getdate()) AS [TenureBand], [Churn]
FROM ( SELECT Inception,
[M01], [M02], [M03], [M04], [M05], [M06],
[M07], [M08], [M09], [M10], [M11]
FROM #TempTable
) pvt
UNPIVOT ( [Churn] FOR #TempTable IN
( [M01], [M02], [M03], [M04], [M05], [M06],
[M07], [M08], [M09], [M10], [M11])
) as unpv
答案 1 :(得分:1)
;WITH cte AS ( /* comment this line out for SQL Server version under 2005 */
SELECT
m.MonthInt,
m.MonthNumber,
DATEDIFF(month, c.inception_dt, @fmonth) AS MonthDiff,
COUNT(
CASE
WHEN m.MonthInt = 1 AND MON01_BASIC_FLAG > 0 THEN 1
WHEN m.MonthInt = 2 AND MON02_BASIC_FLAG > 0 THEN 1
WHEN m.MonthInt = 3 AND MON03_BASIC_FLAG > 0 THEN 1
WHEN m.MonthInt = 4 AND MON04_BASIC_FLAG > 0 THEN 1
WHEN m.MonthInt = 5 AND MON05_BASIC_FLAG > 0 THEN 1
WHEN m.MonthInt = 6 AND MON06_BASIC_FLAG > 0 THEN 1
WHEN m.MonthInt = 7 AND MON07_BASIC_FLAG > 0 THEN 1
WHEN m.MonthInt = 8 AND MON08_BASIC_FLAG > 0 THEN 1
WHEN m.MonthInt = 9 AND MON09_BASIC_FLAG > 0 THEN 1
WHEN m.MonthInt = 10 AND MON10_BASIC_FLAG > 0 THEN 1
WHEN m.MonthInt = 11 AND MON11_BASIC_FLAG > 0 THEN 1
WHEN m.MonthInt = 12 AND MON12_BASIC_FLAG > 0 THEN 1
END
) AS MonthFlagCount
/*INTO #cte*/ /* uncomment this for SQL Server version under 2005 */
FROM (
SELECT 1, 'M01' UNION ALL
SELECT 2, 'M02' UNION ALL
SELECT 3, 'M03' UNION ALL
SELECT 4, 'M04' UNION ALL
SELECT 5, 'M05' UNION ALL
SELECT 6, 'M06' UNION ALL
SELECT 7, 'M07' UNION ALL
SELECT 8, 'M08' UNION ALL
SELECT 9, 'M09' UNION ALL
SELECT 10, 'M10' UNION ALL
SELECT 11, 'M11' UNION ALL
SELECT 12, 'M12'
) AS m (MonthInt, MonthNumber)
CROSS JOIN dbo.customers c ON
GROUP BY m.MonthInt, m.MonthNumber, c.inception_dt
) /* comment this line out for SQL Server version under 2005 */
SELECT
t1.MonthNumber,
CASE
WHEN MonthDiff < 12 THEN '<1yr'
WHEN MonthDiff <= 36 THEN '1-3yr'
WHEN MonthDiff <= 60 THEN '3-5yr'
ELSE '>5yr'
END AS tenureband,
t1.MonthGlagCount - t2.MonthFlagCount AS churn
FROM cte t1
INNER JOIN cte t2 ON t1.MonthInt = t2.MonthInt + 1
/*DROP TABLE #cte*/ /* uncomment this for SQL Server version under 2005 */
编辑:当然,如果最终选择中的SQL Server 2000及更早版本cte
也应替换为#cte
。
答案 2 :(得分:0)
真的,只是笛卡尔加入了一个包含12行的表格。
创建一个名为row_number的列的表。
用12行填充。
将笛卡尔表格放入您的查询中。
选择“M”&amp; ROW_NUMBER
您必须填写row_number以获取M02的0
顺便说一句,生成12个数字的表有一些比较简单的方法,但这是一种简单的学习方法,你以后总是能够重构。答案 3 :(得分:0)
不确定这是否更好,但是当您需要编辑查询时,将它们保持在一起可能有所帮助。
SELECT
M.M AS Monthnumber
,case when DATEDIFF(month,inception_dt,@fmonth)<12 then '<1yr'
when DATEDIFF(month,inception_dt,@fmonth) between 12 and 36 then '1-3yr'
when DATEDIFF(month,inception_dt,@fmonth) between 36 and 60 then '3-5yr'
when DATEDIFF(month,inception_dt,@fmonth)>60 then '>5yr' end as tenureband
,SUM(CASE WHEN CASE M.M
WHEN 'M01' THEN MON1_BASIC_FLAG
WHEN 'M02' THEN MON2_BASIC_FLAG
--..
WHEN 'M11' THEN MON11_BASIC_FLAG
END > 0 THEN 1 ELSE 0 END) -
SUM(CASE WHEN CASE M.M
WHEN 'M01' THEN MON2_BASIC_FLAG
WHEN 'M02' THEN MON3_BASIC_FLAG
--..
WHEN 'M11' THEN MON12_BASIC_FLAG
END > 0 then 1 else 0 end)
as churn
from dbo.customers
cross join (
select 'M01' as M union all select 'M02' union all
select 'M03' union all select 'M04' union all
select 'M05' union all select 'M06' union all
select 'M07' union all select 'M08' union all
select 'M09' union all select 'M10' union all
select 'M11') M
group by inception_dt, M.M
如果您使用的是SQL Server 2008,则可以使用UNPIVOT运算符来提供帮助。
答案 4 :(得分:-1)
您可以这样做:
declare @m int
set @m = 1
while @m <= 12
begin
-- Construct and run dynamic SQL query
set @m = @m + 1
end
答案 5 :(得分:-1)
你可以做下面的事吗?
它不完整但如果您可以加入CTE和现有查询之间,那么您可能可以完成您正在尝试的内容......
declare @count int;
set @count = 1;
WITH Months AS (
SELECT
[Month] = @count
UNION ALL
SELECT
[Month] = [Month] + 1
FROM
Months
WHERE
[Month]< 12)
SELECT 'M' + CAST([Month] as varchar(2))
,case when DATEDIFF(month,inception_dt,@fmonth)<12 then '<1yr'
when DATEDIFF(month,inception_dt,@fmonth) between 12 and 36 then '1-3yr'
when DATEDIFF(month,inception_dt,@fmonth) between 36 and 60 then '3-5yr'
when DATEDIFF(month,inception_dt,@fmonth)>60 then '>5yr' end as tenureband
,SUM(CASE WHEN MON1_BASIC_FLAG >0 THEN 1 ELSE 0 END) -SUM(CASE WHEN MON2_BASIC_FLAG>0 then 1 else 0 end
as churn
from dbo.customers
--JOIN TO Months CTE???
group by inception_dt
OPTION (MAXRECURSION 12)