我有一个表,其中有2个帐户ID,其开始月份和年份与以下数据不同
CREATE TABLE #Temp
(
AccountId NVARCHAR(100),
Churn NVARCHAR(100),
[Month] INT,
[Yr] INT
)
INSERT INTO #Temp
VALUES ('Tst05716825', 'Active', 9, 2016), ('Tst05716825', 'Active', 12, 2016),
('Tst05716825', 'Suspend', 3, 2017), ('Tst05716825', 'Suspend', 8, 2017),
('Tst05716825', 'Terminate', 10, 2017), ('TstNew09567', 'Active', 11, 2017),
('TstNew09567', 'Suspend', 2, 2018), ('TstNew09567', 'Suspend', 4, 2018),
('TstNew09567', 'Terminate', 6, 2018),
('TstNw09567', 'Active', 3, 2016),
('TstNw09567', 'Terminate', 3, 2018);
SELECT *
FROM #Temp
输出如下
AccountId Churn Month Yr
-----------------------------------
Tst05716825 Active 9 2016
Tst05716825 Active 12 2016
Tst05716825 Suspend 3 2017
Tst05716825 Suspend 8 2017
Tst05716825 Terminate 10 2017
TstNew09567 Active 11 2017
TstNew09567 Suspend 2 2018
TstNew09567 Suspend 4 2018
TstNew09567 Terminate 6 2018
TstNw09567 Active 3 2016
TstNw09567 Terminate 3 2018
但是我需要为每个用户开始的月份按以前的值填写缺少的年份和月份,年份将从表中选择第一个月的值。需要输出如下:
AccountId Churn Month Yr
Tst05716825 Active 9 2016
Tst05716825 Active 10 2016
Tst05716825 Active 11 2016
Tst05716825 Active 12 2016
Tst05716825 Active 1 2017
Tst05716825 Active 2 2017
Tst05716825 Suspend 3 2017
Tst05716825 Suspend 4 2017
Tst05716825 Suspend 5 2017
Tst05716825 Suspend 6 2017
Tst05716825 Suspend 7 2017
Tst05716825 Suspend 8 2017
Tst05716825 Suspend 9 2017
Tst05716825 Terminate 10 2017
TstNew09567 Active 11 2017
TstNew09567 Active 12 2017
TstNew09567 Active 1 2018
TstNew09567 Suspend 2 2018
TstNew09567 Suspend 3 2018
TstNew09567 Suspend 4 2018
TstNew09567 Suspend 5 2018
TstNew09567 Terminate 6 2018
TstNw09567 Active 3 2016
TstNw09567 Active 4 2016 till Feb 2018 as Active
TstNw09567 Terminate 3 2018
我需要帮助来解决此问题。我不想使用while循环,因为我们的数据量非常大。
答案 0 :(得分:1)
尝试:我只是使用CURSOR
和MIN MAX
和AccountId
中的每个日期的JOIN
日期来生成日期范围内的日期,重要的是TOP 1
以获取先前的值详细信息
DECLARE @MinDt DATE, @MaxDt DATE, @AccountId VARCHAR(200)
IF OBJECT_ID('tempdb..#dates') IS NOT NULL
DROP TABLE #dates
CREATE TABLE #dates(AccountId VARCHAR(200),dates DATE)
DECLARE b_cursor CURSOR FOR
SELECT DISTINCT AccountId FROM #temp
OPEN b_cursor
FETCH NEXT FROM b_cursor INTO @AccountId
WHILE @@FETCH_STATUS = 0
BEGIN
SELECT
@MinDt = MIN(CAST(CONCAT(Yr,'-',Month,'-',01) AS DATE)),
@MaxDt = MAX(CAST(CONCAT(Yr,'-',Month,'-',01) AS DATE))
FROM #Temp WHERE AccountId = @AccountId
;WITH account_detail(AccountId, account_dates, cnt)AS
(
SELECT @AccountId, @MinDt, 0 AS cnt
UNION ALL
SELECT AccountId,DATEADD(MONTH, (cnt+1), @MinDt), cnt + 1
FROM account_detail r
WHERE DATEADD(MONTH, (cnt+1), @MinDt) <= @MaxDt
)
INSERT INTO #dates(AccountId, dates)
SELECT AccountId, account_dates FROM account_detail
OPTION (MAXRECURSION 0)
FETCH NEXT FROM b_cursor INTO @AccountId
END
CLOSE b_cursor
DEALLOCATE b_cursor
SELECT
ISNULL(te.AccountId,t.AccountId) AS AccountId,
ISNULL(te.Churn, t.Churn) AS Churn,
MONTH(ur.dates) [Month],
YEAR(ur.dates) Yr
FROM #dates ur
LEFT JOIN #temp te ON te.Month = MONTH(ur.dates) AND te.Yr = YEAR(ur.dates)
AND te.AccountId = ur.AccountId
OUTER APPLY(SELECT TOP 1 *
FROM #temp
WHERE CAST(CONCAT(Yr,'-',Month,'-',01) AS DATE) <= ur.dates
AND AccountId = ur.AccountId
ORDER BY CAST(CONCAT(Yr,'-',Month,'-',01) AS DATE) DESC) t
ORDER BY ur.AccountId ASC
输出:
AccountId Churn Month Yr
Tst05716825 Active 9 2016
Tst05716825 Active 10 2016
Tst05716825 Active 11 2016
Tst05716825 Active 12 2016
Tst05716825 Active 1 2017
Tst05716825 Active 2 2017
Tst05716825 Suspend 3 2017
Tst05716825 Suspend 4 2017
Tst05716825 Suspend 5 2017
Tst05716825 Suspend 6 2017
Tst05716825 Suspend 7 2017
Tst05716825 Suspend 8 2017
Tst05716825 Suspend 9 2017
Tst05716825 Terminate 10 2017
TstNew09567 Active 11 2017
TstNew09567 Active 12 2017
TstNew09567 Active 1 2018
TstNew09567 Suspend 2 2018
TstNew09567 Suspend 3 2018
TstNew09567 Suspend 4 2018
TstNew09567 Suspend 5 2018
TstNew09567 Terminate 6 2018
TstNw09567 Active 3 2016
TstNw09567 Active 4 2016
TstNw09567 Active 5 2016
TstNw09567 Active 6 2016
TstNw09567 Active 7 2016
TstNw09567 Active 8 2016
TstNw09567 Active 9 2016
TstNw09567 Active 10 2016
TstNw09567 Active 11 2016
TstNw09567 Active 12 2016
TstNw09567 Active 1 2017
TstNw09567 Active 2 2017
TstNw09567 Active 3 2017
TstNw09567 Active 4 2017
TstNw09567 Active 5 2017
TstNw09567 Active 6 2017
TstNw09567 Active 7 2017
TstNw09567 Active 8 2017
TstNw09567 Active 9 2017
TstNw09567 Active 10 2017
TstNw09567 Active 11 2017
TstNw09567 Active 12 2017
TstNw09567 Active 1 2018
TstNw09567 Active 2 2018
TstNw09567 Terminate 3 2018
答案 1 :(得分:0)
基本上创建一个包含AccountId
的日历cte。因此,您可以获得每个帐户的所有缺失月份和年份(也从该帐户的最小日期到最大日期)。
IF OBJECT_ID('tempdb..#temp') IS NOT NULL
DROP TABLE #temp;
IF OBJECT_ID('tempdb..#calendar') IS NOT NULL
DROP TABLE #calendar;
CREATE TABLE #Temp
(
AccountId NVARCHAR(100) ,
Churn NVARCHAR(100) ,
[Month] INT ,
[Yr] INT
);
INSERT INTO #Temp
VALUES ( 'Tst05716825', 'Active', 9, 2016 ) ,
( 'Tst05716825', 'Active', 12, 2016 ) ,
( 'Tst05716825', 'Suspend', 3, 2017 ) ,
( 'Tst05716825', 'Suspend', 8, 2017 ) ,
( 'Tst05716825', 'Terminate', 10, 2017 ) ,
( 'TstNew09567', 'Active', 11, 2017 ) ,
( 'TstNew09567', 'Suspend', 2, 2018 ) ,
( 'TstNew09567', 'Suspend', 4, 2018 ) ,
( 'TstNew09567', 'Terminate', 6, 2018 ) ,
( 'TstNw09567', 'Active', 3, 2016 ) ,
( 'TstNw09567', 'Terminate', 3, 2018 );
DECLARE @FromDate DATETIME ,
@ToDate DATETIME;
SELECT @FromDate = MIN(CAST(CONCAT(Yr, '-', Month, '-', 01) AS DATE)) ,
@ToDate = MAX(CAST(CONCAT(Yr, '-', Month, '-', 01) AS DATE))
FROM #Temp;
DECLARE @MinDt DATE ,
@MaxDt DATE;
SELECT TOP ( DATEDIFF(MONTH, @FromDate, @ToDate) + 1 ) calendarDate = CAST(DATEADD(
MONTH ,
number ,
@FromDate) AS DATE) ,
Month = MONTH(
DATEADD(
MONTH ,
number ,
@FromDate)) ,
Year = YEAR(
DATEADD(
MONTH ,
number ,
@FromDate))
INTO #calendar
FROM [master].dbo.spt_values
WHERE [type] = N'P'
ORDER BY number;
;WITH AccountCal
AS ( SELECT DISTINCT t.AccountId ,
cal.calendarDate
FROM ( SELECT MAX(calendarDate) AS calendarDate
FROM #calendar c
GROUP BY c.Year ,
c.Month ) cal
CROSS JOIN ( SELECT AccountId ,
MIN(DATEFROMPARTS(Yr, Month, 1)) OVER ( PARTITION BY AccountId ) AS Mindate ,
MAX(DATEFROMPARTS(Yr, Month, 1)) OVER ( PARTITION BY AccountId ) AS Maxdate
FROM #Temp ) t
WHERE cal.calendarDate
BETWEEN t.Mindate AND t.Maxdate )
SELECT cal.AccountId ,
x.Churn ,
MONTH(cal.calendarDate) AS Month ,
YEAR(cal.calendarDate) AS Yr
FROM AccountCal cal
CROSS APPLY ( SELECT TOP 1 Churn
FROM #Temp t
WHERE t.AccountId = cal.AccountId
AND DATEFROMPARTS(t.Yr, t.Month, 1) <= cal.calendarDate
ORDER BY DATEFROMPARTS(t.Yr, t.Month, 1) DESC ) AS x
ORDER BY cal.AccountId ,
cal.calendarDate;
结果:
+-------------+-----------+-------+------+
| AccountId | Churn | Month | Yr |
+-------------+-----------+-------+------+
| Tst05716825 | Active | 9 | 2016 |
| Tst05716825 | Active | 10 | 2016 |
| Tst05716825 | Active | 11 | 2016 |
| Tst05716825 | Active | 12 | 2016 |
| Tst05716825 | Active | 1 | 2017 |
| Tst05716825 | Active | 2 | 2017 |
| Tst05716825 | Suspend | 3 | 2017 |
| Tst05716825 | Suspend | 4 | 2017 |
| Tst05716825 | Suspend | 5 | 2017 |
| Tst05716825 | Suspend | 6 | 2017 |
| Tst05716825 | Suspend | 7 | 2017 |
| Tst05716825 | Suspend | 8 | 2017 |
| Tst05716825 | Suspend | 9 | 2017 |
| Tst05716825 | Terminate | 10 | 2017 |
| TstNew09567 | Active | 11 | 2017 |
| TstNew09567 | Active | 12 | 2017 |
| TstNew09567 | Active | 1 | 2018 |
| TstNew09567 | Suspend | 2 | 2018 |
| TstNew09567 | Suspend | 3 | 2018 |
| TstNew09567 | Suspend | 4 | 2018 |
| TstNew09567 | Suspend | 5 | 2018 |
| TstNew09567 | Terminate | 6 | 2018 |
| TstNw09567 | Active | 3 | 2016 |
| TstNw09567 | Active | 4 | 2016 |
| TstNw09567 | Active | 5 | 2016 |
| TstNw09567 | Active | 6 | 2016 |
| TstNw09567 | Active | 7 | 2016 |
| TstNw09567 | Active | 8 | 2016 |
| TstNw09567 | Active | 9 | 2016 |
| TstNw09567 | Active | 10 | 2016 |
| TstNw09567 | Active | 11 | 2016 |
| TstNw09567 | Active | 12 | 2016 |
| TstNw09567 | Active | 1 | 2017 |
| TstNw09567 | Active | 2 | 2017 |
| TstNw09567 | Active | 3 | 2017 |
| TstNw09567 | Active | 4 | 2017 |
| TstNw09567 | Active | 5 | 2017 |
| TstNw09567 | Active | 6 | 2017 |
| TstNw09567 | Active | 7 | 2017 |
| TstNw09567 | Active | 8 | 2017 |
| TstNw09567 | Active | 9 | 2017 |
| TstNw09567 | Active | 10 | 2017 |
| TstNw09567 | Active | 11 | 2017 |
| TstNw09567 | Active | 12 | 2017 |
| TstNw09567 | Active | 1 | 2018 |
| TstNw09567 | Active | 2 | 2018 |
| TstNw09567 | Terminate | 3 | 2018 |
+-------------+-----------+-------+------+