2009-11-13 86 views
2

我有一个病人表:我希望我能存在关联关系的“内嵌视图”

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  

本质上,患者将在分子中具有1,如果具有用于测量甲的至少一个值和一个VA在这个例子中,患者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 

这个工作过程,只要我不包括相关性,日期限制预期(录音< DATEADD( hh,12,被承认)。不幸的是,以这种方式关联'内联视图'在语法上不是有效的。

这迫使我重新写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 

我的问题:是否有更好的,更有效的方式来做到这一点?

谢谢你的时间。

+0

克雷格的做法:0时01分45秒 – craig 2009-11-13 17:09:22

回答

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的测量 - 但是,你的条款。如果a.cnt或b.cnt为3或更多而另一个为零,则cnt + b.cnt> 2将错误地返回一个。

+0

CodeByMoonlight的做法:00:00:32。优胜者! 感谢您的帮助和纠正。 – craig 2009-11-13 17:13:30

1
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 
+0

它目前缺少日期范围 – MartW 2009-11-13 16:58:56

+0

Quassnoi的方法:00:00:57 – craig 2009-11-13 17:02:42

+0

我在测试中添加了日期范围。 非常感谢您的帮助。伟大的技术。 – craig 2009-11-13 17:14:05

1

另一种解决方案可以使用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) 
0

假设你使用的是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 
0
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