需要根据T-SQL中入院日期列计算出的第一个“3个月”,将一列中的每个值加上3个月

需要根据T-SQL中入院日期列计算出的第一个“3个月”,将一列中的每个值加上3个月,sql,sql-server,date,tsql,dateadd,Sql,Sql Server,Date,Tsql,Dateadd,我有14K记录表,作为与一个特定客户_id=1002相关的数据的以下示例: 我的日期格式是mm/dd/yyyy,以月为先 ClientsEpisodes: client_id adm_date disch_date 1002 3/11/2005 5/2/2005 1002 8/30/2005 2/16/2007 1002 3/16/2017

我有14K记录表,作为与一个特定客户_id=1002相关的数据的以下示例: 我的日期格式是mm/dd/yyyy,以月为先

ClientsEpisodes:

      client_id      adm_date      disch_date    
          1002      3/11/2005        5/2/2005
          1002      8/30/2005       2/16/2007
          1002      3/16/2017            NULL
在SQL Server T-SQL中,我需要将+3个月的日期计算到新列[3个月的日期],其中第一个+3个月的值将从我现有的[adm_date]列中计算出来。然后,[3Months Date]中的值应再加上+3个月,然后接下来的3个月应加上[3Months Date]列中的下一个值,依此类推……,直到[3MonthsDate]您可以使用递归公共表达式。下面是一个例子。请注意,如果需要,您可以使用其他方法更改DATEADD部分,例如添加90天-这是业务逻辑的问题

DECLARE @DataSource TABLE
(
    [client_id] INT
   ,[adm_date] DATE
   ,[disch_date] DATE
);

INSERT INTO @DataSource ([client_id], [adm_date], [disch_date])
VALUES (1002, '3/11/2005 ', '5/2/2005')
      ,(1002, '8/30/2005 ', '2/16/2007')
      ,(1002, '3/16/2017 ', NULL);

WITH DataSource AS
(
    SELECT ROW_NUMBER() OVER(ORDER BY [client_id]) AS [row_id]
          ,[client_id]
          ,[adm_date]
          ,DATEADD(MONTH, 3, [adm_date]) AS [3Month Date]
          ,ISNULL([disch_date], GETUTCDATE()) AS [disch_date]
    FROM @DataSource
    WHERE DATEADD(MONTH, 3, [adm_date]) <= ISNULL([disch_date], GETUTCDATE()) 
),
RecursiveDataSource AS
(
    SELECT [row_id]
          ,[client_id]
          ,[adm_date]
          ,[3Month Date]
          ,[disch_date]
          ,0 AS [level]
    FROM DataSource
    UNION ALL
    SELECT DS.[row_id]
          ,DS.[client_id]
          ,DS.[adm_date]
          ,DATEADD(MONTH, 3, RDS.[3Month Date])
          ,DS.[disch_date]
          ,[level] + 1
    FROM RecursiveDataSource RDS
    INNER JOIN DataSource DS
        ON RDS.[row_id] = DS.[row_id]
        AND DATEADD(MONTH, 3, RDS.[3Month Date]) < DS.[disch_date]
)
SELECT *
FROM RecursiveDataSource
ORDER BY [row_id]
        ,[level];
    
        

这个问题已经有了公认的答案,但您在评论中说,您有性能问题。试试这个,它也简单得多

如果下一行的值取决于上一行的值,则递归CTE非常有用

在这里,我们不需要上一行的答案-我们只需添加n x 3个月,例如,3个月、6个月、9个月,然后过滤您想要保留的行

因此,与其执行递归CTE,不如通过集合逻辑执行

以下是一些数据设置:

CREATE TABLE #Datasource (client_id int, adm_date date, disch_date date);
INSERT INTO #Datasource (client_id, adm_date, disch_date) VALUES
(1002, '20050311', '20050502'),
(1002, '20050830', '20070216'),
(1002, '20170316', NULL),
(1002, '20071105', '20090207');
这里是简单的选择

WITH DataSourceMod AS
    (SELECT client_id, adm_date, disch_date, ISNULL(disch_date, getdate()) AS disc_date_mod
        FROM #Datasource
    ),
Nums_One_to_OneHundred AS 
   (SELECT a * 10 + b AS n
     FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) A(a)
    CROSS JOIN (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) B(b)
    )
SELECT ds.client_id, ds.adm_date, ds.disch_date, DATEADD(month, 3*Nums.n, ds.adm_date) AS ThreeMonthDate
FROM  DataSourceMod ds
    CROSS JOIN Nums_One_to_OneHundred Nums
WHERE DATEADD(month, 3* Nums.n, ds.adm_date) <= ds.disc_date_mod
ORDER BY ds.client_id, ds.adm_date;
这是由

在指定日期或今天计算有效解除日期 计算未来300个月内所有可能的行,请参见表1至表100。。嗯。。将所有值从1到100,然后乘以3。 只取符合日期条件的 如果需要,您可以通过限制需要添加的3个月的数量来进一步优化。这是一个粗略的版本

WITH DataSourceMod AS
    (SELECT client_id, adm_date, disch_date, ISNULL(disch_date, getdate()) AS disc_date_mod, 
            FLOOR(DATEDIFF(month, adm_date, ISNULL(disch_date, getdate())) / 3) + 1 AS nMax 
        FROM #Datasource
    ),
Nums_One_to_OneHundred AS 
   (SELECT a * 10 + b AS n
     FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) A(a)
    CROSS JOIN (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) B(b)
    )
SELECT ds.client_id, ds.adm_date, ds.disch_date, DATEADD(month, 3*Nums.n, ds.adm_date) AS ThreeMonthDate
FROM  DataSourceMod ds
    INNER JOIN Nums_One_to_OneHundred Nums ON Nums.n <= ds.nMax
WHERE DATEADD(month, 3* Nums.n, ds.adm_date) <= ds.disc_date_mod
ORDER BY ds.client_id, ds.adm_date;

因此,如果您的disch_日期比amd_日期大几年,您可能会以10或20列结束?@gotqn不,如果我的disch_日期比adm_日期大几年,我将在[3个月日期]列中有10或20+条记录!正如我的示例中最后一个adm_日期-disch_日期集:adm_日期=2017年3月16日disch_日期=Null current=2020年10月15日,如您所见-最后一个数据集的记录数最大!!!在第23-26行上方突出显示的部分中,为什么不包括下一行,例如,11/5/2008+3个月=2/5/2009,小于2/7/2009。打字错误,还是我错过了什么?@seanb-Typo,我认为只要disch_日期更久-应该继续这么做,以确保这一个将与您上次使用dynamic unpivot时的帮助一样有效!我明天会尝试访问我的服务器,并在这里更新@你认为我们需要在这里添加最大递归选项吗?这取决于案例和数据。这些年来,我没有这样的案例。我也不喜欢递归CTE,因为它很容易损害性能。@gotqn是的。。。我面临着糟糕的表现。我的表有14000多条记录,执行时间是无限的。。。在这里发布另一个问题,并试图研究如何优化…我将在几个小时后检查-目前为afk。非常感谢,我将学习并尝试您的版本,并将在今晚晚些时候分享它的运行情况…与其他帖子/问题中的@Gotqn相同的逻辑,但更短更干净。在他把答案放进去之前,我最初在那里发了帖子,但我把它移到了这里,因为这个答案不是如何使递归CTE更快,而是关于如何真正回答这个原始问题。请注意,我使用内部联接的第二种方法可能不会更快,但值得一试。
WITH DataSourceMod AS
    (SELECT client_id, adm_date, disch_date, ISNULL(disch_date, getdate()) AS disc_date_mod, 
            FLOOR(DATEDIFF(month, adm_date, ISNULL(disch_date, getdate())) / 3) + 1 AS nMax 
        FROM #Datasource
    ),
Nums_One_to_OneHundred AS 
   (SELECT a * 10 + b AS n
     FROM (VALUES (0),(1),(2),(3),(4),(5),(6),(7),(8),(9)) A(a)
    CROSS JOIN (VALUES (1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) B(b)
    )
SELECT ds.client_id, ds.adm_date, ds.disch_date, DATEADD(month, 3*Nums.n, ds.adm_date) AS ThreeMonthDate
FROM  DataSourceMod ds
    INNER JOIN Nums_One_to_OneHundred Nums ON Nums.n <= ds.nMax
WHERE DATEADD(month, 3* Nums.n, ds.adm_date) <= ds.disc_date_mod
ORDER BY ds.client_id, ds.adm_date;