如何使用基于用户帐户ID的先前值填补SQL Server中的空白

如何使用基于用户帐户ID的先前值填补SQL Server中的空白,sql,sql-server,sql-server-2012,Sql,Sql Server,Sql Server 2012,我有两个帐户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), ('Tst

我有两个帐户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循环,因为我们的数据量非常大

试试这个:我只是用
游标
帐户ID
加入
的日期来生成范围内的日期,最重要的是
前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
的日历cte。因此,您可以获得每个帐户缺少的所有月份和年份(也可以是该帐户从最小日期到最大日期)


谢谢,但我想要缺少月份的上一个值。在您的示例中,将客户流失值设置为活动。不是活动的,而是想要以前的值。对不起,兄弟,比较日期之前是为了测试目的硬编码的,发布时忘记了更改列名。请立即检查。如果月份和年份与其他帐户重叠,则会失败。如果我们在逻辑填充失败上方添加具有新帐户ID的2行新行(“TstNw09567”,“活动”,2016年3月3日),(“TstNw09567”,“终止”,2018年3月3日)(你能告诉我失败的地方和原因吗?我已经用你更改的数据验证过了,它运行得非常好。我是否遗漏了什么?我应该获取TstNw09567此用户数据,从2016年3月到2018年2月处于活动状态,这意味着总共有24行处于活动状态,一行终止。对于同一帐户。但我仅获取TstNw09567的7行。在t之后从2016年9月开始,它将丢弃bcoz另一个帐户id Tst05716825。如果我们在同一年和同一个月有另一个用户,则此逻辑将失败。它仅显示一个用户详细信息。好的。我已更新了我的答案。使用原始测试数据遗漏了答案。感谢您的帮助。是的,我理解您希望生成日期
帐户的要求Id
以保持差距。但是
这与最初的要求有很大不同
,并且不确定这是否是您的最终要求?感谢您的回复。这是我的最终要求,正如我在主题“按帐户Id填补差距”中提到的这条线索的开始部分。是的,在添加更多数据后,它是清晰的,但在仅检查预期数据时,它不是这样清晰,不幸的是,我错过了文本
AccountId的差距
。请检查下面更新的答案。
    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
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 |
+-------------+-----------+-------+------+