简体   繁体   English

需要根据 T-SQL 中的“入学日期”列计算出的第一个“3 个月”为列中的每个值添加 3 个月

[英]Need to add 3 months to each value within a column, based on the 1st '3 Months' calculated off the Admission Date column in T-SQL

I have 14K records table as the following (example of the data related to one particular client_id = 1002): (my date format is mm/dd/yyyy, months come first)我有 14K 记录表如下(与一个特定的 client_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

In SQL Server (T-SQL) - I need to calculate + 3 months date into the new column [3Month Date], where the 1st "+ 3 months" value will be calculated off my existing [adm_date] column.在 SQL Server (T-SQL) 中 - 我需要在新列 [3Month Date] 中计算 + 3 个月的日期,其中第一个“+ 3 个月”值将根据我现有的 [adm_date] 列计算。 Then + 3 more months should be added to the value in [3Months Date], then the next 3 months should be added to the next value in the [3Months Date] column, and so on..., until [3MonthsDate] <= [disch_date].然后 + 3 个月应该添加到 [3Months Date] 中的值,然后接下来的 3 个月应该添加到 [3Months Date] 列中的下一个值,依此类推...,直到 [3MonthsDate] <= [disch_date]。 When [3Months Date] is more than [disch_date] then the data shouldn't be populated.当 [3Months Date] 大于 [disch_date] 时,不应填充数据。 If my [disch_date] IS NULL then the condition should be [3Months Date] <= current date (whatever it is) from GETDATE() function.如果我的 [disch_date] 为 NULL,那么条件应该是 [3Months Date] <= 当前日期(无论它是什么)来自 GETDATE() 函数。

Here is what I expect to see as a result: (I highlighted my dates offsets with different colors, for a better view)这是我希望看到的结果:(我用不同的颜色突出显示了我的日期偏移量,以便更好地查看)

在此处输入图片说明

Below, I'll clarify with more detailed explanation, about each populated (or not populated) data set:下面,我将详细解释每个填充(或未填充)的数据集:

My first [adm_date] from ClientsEpisode table was 3/11/2005.我在 ClientsEpisode 表中的第一个 [adm_date] 是 2005 年 3 月 11 日。 Adding 3 months: 3/11/2005 + 3 months = 6/11/2005 - falls AFTER the initial [disch_date] (5/2/2005) - not populated添加 3 个月:3/11/2005 + 3 个月 = 6/11/2005 - 在初始 [disch_date] (5/2/2005) 之后下降 - 未填充

   Next [adm_date] from ClientEpisode is 8/3/2005 + 3 Months = 11/30/2005; 
        then + 3 months must be added to 11/30/2005 = 2/30/2006; 
        then 2/30/2006 + 3 months = 5/30/2006; 
        then 5/30/2006 + 3 months = 8/30/2006; 
        then 8/30/2006 + 3 months = 11/30/2006;
        then 11/30/2006 + 3 months = 3/2/2007 - falls AFTER my [disch_date] 
                                                      (2/16/2007) - not populated

the same algorithm for the next [adm_date] - [disch_date] sets 11/5/2007-2/7/2009 (in dark blue).相同的算法用于下一个 [adm_date] - [disch_date] 设置 11/5/2007-2/7/2009(深蓝色)。

then, where [adm_date] = 3/16/17, I have [disch_date] IS NULL, so, the algorithm applies until [3 Months Date] <= current date (10/15/2020 in this case)然后,在 [adm_date] = 3/16/17,我有 [disch_date] IS NULL,因此,该算法适用于 [3 Months Date] <= 当前日期(在这种情况下为 10/15/2020)

You can use recursive common expression .您可以使用recursive common expression Below is an example.下面是一个例子。 Note, that you can change the DATEADD part with other (for example add 90 days if you want) - it's a matter of bussness logic.请注意,您可以将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];
    
        

This question already has an accepted answer, but you say in the comments for that, that you have performance problems.这个问题已经有一个公认的答案,但你在评论中说你有性能问题。 Try this instead - it's also a lot simpler.试试这个 - 它也简单得多。

A recursive CTE is really useful if the value of the next row depends on the value of the previous row.如果下一行的值取决于前一行的值,递归 CTE 真的很有用。

Here, we don't need the answer to the previous row - we just add n x 3 months (eg, 3 months, 6 months, 9 months) and filter the rows you want to keep.在这里,我们不需要前一行的答案 - 我们只需添加n x 3 个月(例如,3 个月、6 个月、9 个月)并过滤您想要保留的行。

Therefore, instead of doing a recursive CTE, just do it via set logic.因此,与其进行递归 CTE,不如通过设置逻辑进行。

Here's some data setup:下面是一些数据设置:

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');

And here's the simple SELECT这是简单的 SELECT

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;

This works by这工作由

  • Calculating the effective discharge date (the specified date, or today)计算有效出院日期(指定日期,或今天)
  • Calculating all possible rows for up to 300 months in the future (the table One_to_OneHundred .. um.. has all the values from 1 to 100, then multiplied by 3.)计算未来最多 300 个月的所有可能行(表 One_to_OneHundred .. um.. 具有从 1 到 100 的所有值,然后乘以 3。)
  • Only taking those that fulfil the date condition只取那些满足日期条件的

You can further optimise this if desired, by limiting the number of 3 months you need to add.如果需要,您可以通过限制需要添加的 3 个月数来进一步优化此设置。 Here's a rough version.这是一个粗略的版本。

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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM