sql - 如何将此 T-SQL 游标操作转换为基于集合的操作

标签 sql sql-server sql-server-2012

我有一个医疗报告项目,需要根据诊断日期列表计算“发病”日期。

患者第一次出现某种诊断时,即发病日期#1。在接下来的 30 天内,任何后续诊断日期都算作第 1 次发作的一部分,因此可以删除这些后续日期。

30 天过去后,下一次诊断日期算作第 2 次发作。

等等..

示例输入

2015-09-10 (this is onset #1)
2015-09-19 (within 30 days from onset #1, so drop)
2015-09-29 (within 30 days from onset #1, so drop)
2015-10-17 (>= 30 days from onset #1, so this is onset #2)
2015-10-19 (within 30 days from onset #2, so drop)
2015-11-13 (within 30 days from onset #2, so drop)
2015-11-29 (>= 30 days from onset #2, so this is onset #3)

示例输出

2015-09-10 (onset #1)
2015-10-17 (onset #2)
2015-11-29 (onset #3)

这可以用游标来完成,下面包含一个最小的例子。

我听说任何游标操作都可以表示为基于集合的操作。但是我无法弄清楚如何以基于集合的方式处理这个特定的算法,因为计算依赖于以前的算法。我看不出它是如何在一次基于集合的“通过”中完成的。

可以吗?如果是,怎么办?

任何解决方案都应该适用于 SQL Server 2012。

DECLARE @dx_list TABLE(dx_dt date);
INSERT INTO @dx_list(dx_dt)
    VALUES ('2015-09-10') --this is onset #1
    , ('2015-09-19') 
    , ('2015-09-29')
    , ('2015-10-17') --date is >= 30 days from last onset, so this is onset #2
    , ('2015-10-19')
    , ('2015-11-13')
    , ('2015-11-29'); --date is >= 30 days from last onset, so this is onset #3

DECLARE @mycursor AS cursor;
SET @mycursor = CURSOR FOR
SELECT dx_dt
FROM @dx_list
ORDER BY dx_dt; --make sure dates are in order

DECLARE @possible_dt AS date;
DECLARE @onset_list TABLE(onset_dt date);

OPEN @mycursor;
FETCH NEXT FROM @mycursor INTO @possible_dt;
--First date is always an onset date
INSERT INTO @onset_list(onset_dt) VALUES (@possible_dt);

WHILE @@FETCH_STATUS = 0
BEGIN
    FETCH NEXT FROM @mycursor INTO @possible_dt;
    --If this date is 30 days or more from last onset date, add it
    IF @possible_dt >= DATEADD(dd, 30, (SELECT MAX(onset_dt) FROM @onset_list))
    BEGIN
        INSERT INTO @onset_list(onset_dt) VALUES (@possible_dt);
    END
END

CLOSE @mycursor;
DEALLOCATE @mycursor;

--Show results
SELECT * FROM @onset_list;

最佳答案

您可以使用 Recursive CTE 来做到这一点:

;WITH OnsetDates AS (
   SELECT TOP 1 dx_dt
   FROM dx_list
   ORDER BY dx_dt

   UNION ALL

   SELECT dx_dt
   FROM (  
     SELECT d1.dx_dt,
            ROW_NUMBER() OVER (ORDER BY d1.dx_dt) AS rn
     FROM dx_list AS d1
     INNER JOIN OnsetDates AS d2 ON d1.dx_dt > d2.dx_dt
     WHERE DATEDIFF(d, d2.dx_dt, d1.dx_dt) >= 30 ) AS t
   WHERE t.rn = 1
)
SELECT *
FROM OnsetDates

CTE 的所谓锚定成员 只是顶级日期。 递归成员 获取下一个开始日期:这是过去 30 天的第一个日期 + 上次调用递归 CTE 返回的日期。

请注意,为了获得这个第一个日期,我们必须使用ROW_NUMBER 和一个子查询,因为TOP 1ORDER BY 不允许出现在 CTE 的递归成员中。

Demo here

关于sql - 如何将此 T-SQL 游标操作转换为基于集合的操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/32507962/

相关文章:

c# - 无法连接到 SQL Server 2014 Express

version-control - 使用 Visual Studio Online (VSO) 进行 SQL Server 源代码管理

sql-server-2012 - 如何在 SQL Server 2014 中使用带有 case 的 if 语句

sql - Datediff 函数仅在 WHERE 子句中导致溢出?

sql - 查询返回 MS SQL Server 列出的所有系统存储过程

sql - 使用 levenshtein 有条件地加入 Postgres

sql - 连接多个表的连接表

c# - 按字段排序,该字段是 NHibernate/LINQ 中其他两个字段的组合/函数

mysql - 加载数据库文件时出现 SQL 语法错误(可能是版本错误?)

sql - 数据库列中的位标志有什么缺点吗?