需要根据 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
我有 14K 条记录 table 如下(与特定 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
在 SQL 服务器 (T-SQL) - 我需要计算 + 3 个月日期到新列 [3Month Date],其中第一个“+ 3 个月”值将是根据我现有的 [adm_date] 列计算。然后 + 3 个月应添加到 [3Months Date] 中的值,然后接下来的 3 个月应添加到 [3Months Date] 列中的下一个值,依此类推......,直到 [3MonthsDate] <= [disch_date]。当 [3Months Date] 超过 [disch_date] 时,不应填充数据。如果我的 [disch_date] IS NULL 那么条件应该是
[3 个月日期] <= 来自 GETDATE() 函数的当前日期(无论它是什么)。
这是我希望看到的结果:
(我用不同的颜色突出显示了我的日期偏移量,以便更好地查看)
下面,我将对每个填充(或未填充)数据集进行更详细的解释:
我第一次 [adm_date] 来自 ClientsEpisode table 是 3/11/2005。
添加3个月:
2005 年 3 月 11 日 + 3 个月 = 2005 年 6 月 11 日 - 在初始 [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
下一个 [adm_date] 的相同算法 - [disch_date] 设置 11/5/2007-2/7/2009(深蓝色)。
然后,其中 [adm_date] = 3/16/17,我有 [disch_date] IS NULL,因此,该算法适用直到
[3 个月日期] <= 当前日期(在本例中为 10/15/2020)
您可以使用 recursive common expression
。下面是一个例子。请注意,您可以将 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');
这是简单的 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;
这是
的作品
- 正在计算有效出院日期(指定日期,或今天)
- 计算未来最多 300 个月的所有可能行(table One_to_OneHundred .. um.. 具有从 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;
我有 14K 条记录 table 如下(与特定 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
在 SQL 服务器 (T-SQL) - 我需要计算 + 3 个月日期到新列 [3Month Date],其中第一个“+ 3 个月”值将是根据我现有的 [adm_date] 列计算。然后 + 3 个月应添加到 [3Months Date] 中的值,然后接下来的 3 个月应添加到 [3Months Date] 列中的下一个值,依此类推......,直到 [3MonthsDate] <= [disch_date]。当 [3Months Date] 超过 [disch_date] 时,不应填充数据。如果我的 [disch_date] IS NULL 那么条件应该是 [3 个月日期] <= 来自 GETDATE() 函数的当前日期(无论它是什么)。
这是我希望看到的结果: (我用不同的颜色突出显示了我的日期偏移量,以便更好地查看)
下面,我将对每个填充(或未填充)数据集进行更详细的解释:
我第一次 [adm_date] 来自 ClientsEpisode table 是 3/11/2005。 添加3个月: 2005 年 3 月 11 日 + 3 个月 = 2005 年 6 月 11 日 - 在初始 [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
下一个 [adm_date] 的相同算法 - [disch_date] 设置 11/5/2007-2/7/2009(深蓝色)。
然后,其中 [adm_date] = 3/16/17,我有 [disch_date] IS NULL,因此,该算法适用直到 [3 个月日期] <= 当前日期(在本例中为 10/15/2020)
您可以使用 recursive common expression
。下面是一个例子。请注意,您可以将 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');
这是简单的 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;
这是
的作品- 正在计算有效出院日期(指定日期,或今天)
- 计算未来最多 300 个月的所有可能行(table One_to_OneHundred .. um.. 具有从 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;