从另一个结果集中拉取结果

Pulling results from another result set

我正在处理一个查询,我需要从某个诊所提取所有吸烟或使用某种烟草的患者的列表。然后,我需要列出一份清单,显示在给定时间范围内接受过戒烟咨询的 那些 患者。

认为我这样做是对的,但我不确定。前端系统使我们经常以不准确的数据结束(甚至不要让我开始)。当我 运行 按原样查询时,我得到一个数字,我很确定它太低了,但我不确定它是否只是数据的问题,或者我的查询是否有问题.这是我得到的。

首先,这是我查找所有烟草用户的查询。 "status" 列表示使用类型——您会在这里看到我过滤掉了 3、4 和 6,它们代表 "former user," "never used," 和 "unknown"(我只是在看那些肯定正在使用烟草的人——这些条目会在患者每次来访时更新(如果适用))。

SELECT DISTINCT sh.PatientID, sh.Description, sh.Category, sh.Status, pd.Physician, vi.VisitDate

FROM SocialHistory sh JOIN PatientDemographic pd ON sh.PatientID = pd.PatientID 
     JOIN VisitInfo vi ON vi.PatientID = pd.PatientID

WHERE sh.Description LIKE '%tobacco%'
     AND sh.Status != 3
     AND sh.Status != 4
     AND sh.Status != 6

那个查询给了我 3000 多个结果,考虑到诊所的总患者人数,这似乎是正确的。

现在我需要从那个结果集中提取所有接受过戒烟咨询的人(作为 程序代码输入 - 我将该字段添加到 select列表)在 2016 年 1 月 1 日和 2016 年 6 月 30 日之间。这是我所拥有的:

SELECT DISTINCT sh.PatientID, vi.ProcedureCode, pd.Physician, vi.VisitDate

FROM
    (SELECT DISTINCT sh.PatientID, sh.Description, sh.Category, sh.Status, 
     pd.Physician, vi.VisitDate, vi.ProcedureCode

     FROM SocialHistory sh JOIN PatientDemographic pd ON sh.PatientID = pd.PatientID 
     JOIN VisitInfo vi ON vi.PatientID = pd.PatientID

     WHERE sh.Description LIKE '%tobacco%'
     AND sh.Status != 3
     AND sh.Status != 4
     AND sh.Status != 6

    ) VisitInfo

WHERE vi.ProcedureCode IN ('counseling1','counseling2','counseling3')

AND VisitDate BETWEEN '01/01/2016' AND '06/30/2016'

我只得到大约 190 个结果,考虑到 3000 多名烟草使用者,这似乎真的很低。但它可能是准确的。我只是想确保我的查询正确完成。嵌套 SELECT 语句的结构是否正确?

这是另一种编写查询的方法,它更具可读性并且不需要子 select。

SELECT DISTINCT
   sh.PatientID
   ,vi.ProcedureCode
   ,pd.Physician
   ,vi.VisitDate
 FROM
   SocialHistory sh
   INNER JOIN PatientDemographic pd
   ON sh.PatientID = pd.PatientID 
   INNER JOIN VisitInfo vi
   ON vi.PatientID = pd.PatientID
   AND vi.ProcedureCode IN ('counseling1','counseling2','counseling3')
   AND VisitDate BETWEEN '01/01/2016' AND '06/30/2016'
 WHERE
   sh.Description LIKE '%tobacco%'
   AND sh.Status NOT IN (3,4,6)

6 个月更有可能是您记录数量较少的原因,请扩展该时间范围或对该行进行注释以测试您的结果集,看看它是否与您认为的结果一致应该。如果是这样,那么您就知道是 6 个月了。

只是一个改进编码的建议。计算机擅长记忆事物的代码;人们不是。必须为 'former smoker' 输入“2”或为 'lives in Minnesota' 输入“5”是荒谬的。应该有一个 TABLE 将代码与实际状态相关联,因此您可以在需要时进行查找。或者使用短字符串代码,例如 'FSM' 代表 'former smoker' 或 'LMN' 代表明尼苏达州的生活:诸如此类。

我没有发现您的查询有任何不正确之处,但正如 Matt 指出的那样,您不需要子查询。我会这样写,

SELECT DISTINCT sh.PatientID, vi.ProcedureCode, pd.Physician, vi.VisitDate
FROM SocialHistory as sh
JOIN PatientDemographic as pd ON sh.PatientID = pd.PatientID 
JOIN VisitInfo as vi          ON vi.PatientID = pd.PatientID
WHERE
    sh.Description LIKE '%tobacco%'
AND sh.Status not in (3, 4, 6)
AND vi.ProcedureCode IN ('counseling1','counseling2','counseling3')
AND VisitDate BETWEEN '01/01/2016' AND '06/30/2016'

因为 JOIN 仅用于匹配条件,而 WHERE 用于限制。 (我相信 SQL 服务器会为两个查询显示相同的查询计划,也可能为您的查询显示相同的查询计划。)

当你确实使用子查询时,避免在其中使用 DISTINCT 除非它在语义上很重要。在您的查询中,您得到了不同的患者,然后是一组不同的 those 患者。只需要一个。查询规划器应该找到一条捷径,但可能不会,任何阅读您的 SQL 的人都会有更多的噪音需要看穿。

关于故障排除,您可以尝试这样的操作以查看您正在处理的问题:

select   count(distinct PatientID) as N, M, Y
from (
    SELECT   sh.PatientID, vi.ProcedureCode, pd.Physician, vi.VisitDate
           , year(vi.VisitDate) as Y
           , month(vi.VisitDate) as M
    FROM SocialHistory as sh
    JOIN PatientDemographic as pd ON sh.PatientID = pd.PatientID 
    JOIN VisitInfo as vi          ON vi.PatientID = pd.PatientID
    WHERE
        sh.Description LIKE '%tobacco%'
    AND sh.Status not in (3, 4, 6)
    AND vi.ProcedureCode IN ('counseling1','counseling2','counseling3')
    --- VisitDate BETWEEN '01/01/2016' AND '06/30/2016'
) as V
group by M, Y

如果这太多而无法查看,请改为仅按 Y 分组,或者 where M = 1

在这里,试试下面。由于您只需要接受过咨询的患者,因此这将 return 患者和提供者数据。如果您想要那些也没有接受过咨询的患者,只需注释掉 IsCounselledStatus > 0 部分,每个带有“0”的人都没有接受过咨询。

declare @StartDate datetime = '1/1/'+cast(datepart(YEAR,getdate()) as varchar(4));
declare @EndDate datetime = '6/30/'+cast(datepart(YEAR,getdate()) as varchar(4));

;with cte as
(
    select sh.PatientID, sh.Description, sh.Category, sh.Status, pd.Physician, vi.VisitDate, vi.ProcedureCode,
        case when (sh. status not in (3,4,6) 
                    and sh.Description LIKE '%tobacco%' 
                    and VisitDate BETWEEN @StartDate AND @EndDate
                    and vi.ProcedureCode IN ('counseling1','counseling2','counseling3')
                   ) then 1 else 0 end as IsCounselled
    from SocialHistory sh JOIN PatientDemographic pd ON sh.PatientID = pd.PatientID 
    join VisitInfo vi ON vi.PatientID = pd.PatientID 
)
select PatientID, Physician, sum(IsCounselled) IsCounselledStatus
from cte c
where IsCounselledStatus > 0
group by PatientID, Physician

此外,正如 David 指出的那样,如果您的参考资料放在 table 中会更好。