【问题标题】:I wish I could correlate an "inline view"我希望我可以关联一个“内联视图”
【发布时间】:2010-12-16 08:44:56
【问题描述】:

我有一张患者表:

PatientId   Admitted
---------   ---------------
1           d/m/yy hh:mm:ss
2           d/m/yy hh:mm:ss
3           d/m/yy hh:mm:ss

我有一个 PatientMeasurement 表(0 到多个):

PatientId   MeasurementId   Recorded            Value
---------   -------------   ---------------     -----
1           A               d/h/yy hh:mm:ss     100
1           A               d/h/yy hh:mm:ss     200
1           A               d/h/yy hh:mm:ss     300
2           A               d/h/yy hh:mm:ss     10
2           A               d/h/yy hh:mm:ss     20
1           B               d/h/yy hh:mm:ss     1
1           B               d/h/yy hh:mm:ss     2

我正在尝试创建类似于以下内容的结果集:

PatientId   Numerator   Denominator
---------   --------    -----------
1           1           1
2           1           1
3           0           1       

本质上,如果患者 A 测量值至少有一个值,B 测量值至少有一个值,则患者的分子将是 1。在此示例中,患者 1 具有 3 个 A 测量值和 2 个 B 测量值,因此分子为1. 患者 2 有 2 个 A 测量值,但没有 B 测量值,因此分子为 0。患者既没有 A 测量值也没有 B 测量值,因此分子为 0。

到目前为止我的查询是:

SELECT  PatientId, CASE WHEN a.cnt+b.cnt>2 THEN 1 ELSE 0 END Numerator, 1 Denominator
FROM    patient p

LEFT OUTER JOIN (
    SELECT  PatientId, count(*) cnt
    FROM    PatientMeasurement pm
    WHERE   MeasurementId='A'
    --AND   Recorded <= dateadd(hh, 12, Admitted)
    GROUP BY PatientId
) a ON p.PatientId=a.PatientId

LEFT OUTER JOIN (
    SELECT  PatientId, count(*) cnt
    FROM    PatientMeasurement pm
    WHERE   MeasurementId='B'
    --AND   Recorded <= dateadd(hh, 12, Admitted)
    GROUP BY PatientId
) b ON p.PatientId=b.PatientId

只要我不包含相关的日期限制(Recorded

这迫使我将 SQL 重写为:

SELECT  PatientId, CASE WHEN v.a+v.b>2 THEN 1 ELSE 0 END Numerator, 1 Denominator
FROM    (

    SELECT  PatientId,
    (
        SELECT  PatientId, count(*) cnt
        FROM    PatientMeasurement pm
        WHERE   PatientId=p.PatientId
        AND MeasurementId='A'
        AND Recorded <= dateadd(hh, 12, Admitted)
        GROUP BY PatientId
    ) a,
    (
        SELECT  PatientId, count(*) cnt
        FROM    PatientMeasurement pm
        WHERE   PatientId=p.PatientId
        AND MeasurementId='B'
        AND Recorded <= dateadd(hh, 12, Admitted)
        GROUP BY PatientId
    ) b
    FROM    Patient p
) v

我的问题:有没有更好、更有效的方法来做到这一点?

感谢您的宝贵时间。

【问题讨论】:

    标签: sql sql-server syntax derived-table inline-view


    【解决方案1】:

    试试这个:

    WITH GroupPatients AS 
        (SELECT MeasurementID, PatientId, Count(*) AS cnt
        FROM PatientMeasurement AS pm
        INNER JOIN Patient p ON pm.PatientID = p.PatientID
        WHERE
            MeasurementId IN ('A', 'B')
        AND
            Recorded <= dateadd(hh, 12, Admitted)
        GROUP BY MeasureMentID, PatientId)
    
    SELECT p.PatientID, Case
        When IsNull(GPA.cnt, 0) > 0 AND IsNull(GPB.cnt, 0) > 0 Then 1
        Else 0
    End AS Numerator, 1 AS Denominator
    FROM Patient p
    LEFT JOIN GroupPatientsA AS GPA ON p.PatientID = GPA.PatientID AND GPA.MeasurementID = 'A'
    LEFT JOIN GroupPatientsB AS GPB ON p.PatientID = GPB.PatientID AND GPB.MeasurementID = 'B'
    

    我也对业务逻辑进行了一次调整 - 您的规范说如果患者同时具有 A 和 B 测量值,Numerator 应该是一个 - 但是,您的 a.cnt+b.cnt>2 子句将错误地返回一个如果 a.cnt 或 b.cnt 中的一个为 3 或更大,而另一个为 0。

    【讨论】:

    • CodeByMoonlight 的方法:00:00:32。优胜者!感谢您的帮助和更正。
    【解决方案2】:

    另一个解决方案可以接近您使用OUTER APPLY 的原始尝试:

    SELECT  PatientId, CASE WHEN a.cnt+b.cnt>2 THEN 1 ELSE 0 END Numerator, 1 Denominator 
    FROM    patient p 
    OUTER APPLY ( 
        SELECT      count(*) cnt 
        FROM        PatientMeasurement pm 
        WHERE       MeasurementId='A' 
        AND       Recorded <= dateadd(hh, 12, p.Admitted) 
        AND pm.PatientId = p.PatientId
    ) AS a(cnt)     
    OUTER APPLY ( 
        SELECT      count(*) cnt 
        FROM        PatientMeasurement pm 
        WHERE       MeasurementId='B' 
        AND       Recorded <= dateadd(hh, 12, p.Admitted) 
        AND pm.PatientId = p.PatientId
    ) AS b(cnt)
    

    【讨论】:

      【解决方案3】:
      SELECT  p.*, 
              CASE WHEN
              EXISTS
              (
              SELECT  NULL
              FROM    PatientMeasurement pm
              WHERE   pm.PatientID = p.ID
                      AND pm.Type = 'A'
                      AND pm.Recorded <= DATEADD(hh, 12, p.Admitted)
              ) AND EXISTS (
              SELECT  NULL
              FROM    PatientMeasurement pm
              WHERE   pm.PatientID = p.ID
                      AND pm.Type = 'B'
                      AND pm.Recorded <= DATEADD(hh, 12, p.Admitted)
              ) THEN 1 ELSE 0 END
      FROM    Patient p
      

      【讨论】:

      • 目前缺少日期范围
      • Quassnoi 的方法:00:00:57
      • 我在测试中添加了日期范围。非常感谢你的帮助。很棒的技术。
      • EXISTS 非常有用。我有点惊讶我的查询出来得更快。
      【解决方案4】:

      假设您使用的是 Sql 2005 或 2008,则可以使用一些窗口函数和枢轴来简化整个查询:

      with pData as
      (
          select  count(*) over(partition by PatientId, MeasurementId) as cnt,
                  PatientId, MeasurementId
          from    PatientMeasurement pm
          where   MeasurementId in('A','B')
          and     Recorded <= dateadd(hh, 12, Admitted)
      )
      select  PatientId, coalesce([A],0) as cntA, coalesce([B],0) as cntB,
              case when coalesce([A],0) + coalesce([B],0) > 2 then 1 else 0 end as Numerator,
              1 as Denominator
      from    pData
      pivot   (max(cnt) for MeasurementId in([A],[B])) pvt
      

      【讨论】:

        【解决方案5】:
        DECLARE @TimeSlot int;
        SET @TimeSlot = 12;
        
        WITH 
        pt AS (
            SELECT p.PatientID, p.Admitted, m.MeasurementID, m.Recorded,
                CASE 
                  WHEN m.Recorded <= dateadd(hh, @TimeSlot, p.Admitted) THEN 1 
                  ELSE 0 
                END AS "InTimeSlot"
            FROM Patient AS p
            LEFT JOIN PatientMeasurement AS m ON p.PatientID = m.PatientID
        ),
        cntA AS (
            SELECT PatientID, count(*) AS "A_count"
            FROM pt WHERE MeasurementID='A' AND InTimeSlot = 1
            GROUP BY PatientID
        ),
        cntB AS (
            SELECT PatientID, count(*) AS "B_count"
            FROM pt WHERE MeasurementID='B' AND InTimeSlot = 1
            GROUP BY PatientID
        ),
        cntAB AS (
            SELECT p.PatientID
                  ,coalesce(a.A_count, 0) AS "A_cnt"
                  ,coalesce(b.B_count, 0) AS "B_cnt"
            FROM Patient as p
            LEFT JOIN  cntA  AS a ON p.PatientID = a.PatientID
            LEFT JOIN  cntB  AS b ON p.PatientID = b.PatientID
        ),
        cntN AS (
            SELECT PatientID,
                CASE WHEN A_cnt > 0 AND B_cnt > 0 THEN 1 ELSE 0 END AS Numerator
            FROM cntAB 
        )
        SELECT PatientID, Numerator, 1 AS Denominator FROM cntN
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2014-05-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多