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

时间:2016-07-06 16:42:36

标签: sql sql-server sql-server-2012 resultset

我正在进行一项查询,我需要从某个吸烟或使用某种烟草的诊所中提取所有患者的清单。然后,我需要提供一个列表,显示在给定时间范围内已经进行过戒烟咨询的那些患者。

认为我做得对,但我不确定。前端系统经常会出现不准确的数据(甚至不让我开始)。当我按照它的方式运行查询时,我得到一个我非常确定太低的数字,但是我不确定它是否与数据有关或者我的查询是否存在问题。这就是我所拥有的。

首先,这是我查询所有烟草使用者的查询。 “状态”列表示使用类型 - 您将在此处看到我正在过滤掉3,4和6,它们代表“前用户”,“从未使用过”和“未知”(I'我只是看那些肯定会使用烟草的人 - 每次患者就诊时,这些条目都会得到更新(如适用)。

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个结果,考虑到诊所的总患者人数,这似乎是正确的。

现在我需要从2016年1月1日到6月之间已经停止咨询的结果集(在程序代码 - 我将该字段添加到选择列表中)中拉出所有人30,2016。这就是我所拥有的:

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语句是否正确结构化?

4 个答案:

答案 0 :(得分:3)

我发现您的查询不正确,但正如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 Server将为这两个查询显示相同的查询计划,也可能为您的查询计划。)

当您使用子查询时,请在其中避开DISTINCT,除非它具有语义上的重要性。在您的查询中,您将获得不同的患者,然后是一组独特的那些患者。只需要一个。查询计划程序应该找到一个快捷方式,但可能不会,并且任何阅读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

答案 1 :(得分:1)

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

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个月。

答案 2 :(得分:1)

在这里,请尝试以下。由于您只需要咨询的患者,这将返回患者和提供者数据。如果您想要那些也没有得到咨询的患者,只需注明IsCounselledStatus > 0部分和每个人的“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

另外,正如大卫指出的那样,如果你在桌子上有参考物品会更好。

答案 3 :(得分:0)

只是建议改进您的编码。计算机擅长记忆事物的代码;人不是。必须输入' 2'对于前吸烟者'或者' 5'为了生活在明尼苏达州'太荒谬了。应该有一个表将代码与实际状态联系起来,因此如果需要,您可以查看。要么是这样,要么使用短字符串代码,例如FSM'对于前吸烟者'或者' LMN'对于明尼苏达州的生活:那种事。