NOT IN条件返回意外结果

时间:2018-10-16 07:19:53

标签: sql sql-server

我们正在尝试创建一个报告,以识别计划中没有任何未来约会的活跃患者。活跃的患者将是过去一年内约好的患者。我的想法是创建一个包含所有在过去一年内就诊的患者的临时表,然后使用NOT IN条件将其从我的下一个查询中排除。效果不理想。

以下查询列出了去年所有未取消或未出现的患者。

SELECT Patient_ID
INTO #TempPreviousAppt
FROM Appointments
WHERE Appointment_DateTime > DATEADD(year,-1,GETDATE())
AND Status NOT IN ('X','N')
GROUP BY Patient_ID

这将产生48,489个结果

以下查询列出了所有将来要预约的患者。

SELECT Patient_ID
INTO #TempFutureAppt
FROM Appointments AS Appt
WHERE Appt.Appointment_DateTime > GETDATE()
AND Status != 'S'
GROUP BY Patient_ID

这将产生4,683个结果

当我尝试使用以下查询来确定以前见过的患者是否有未来的预定约会时。

SELECT Patient_ID 
FROM #TempPreviousAppt
WHERE Patient_ID NOT IN
(SELECT ISNULL(Patient_ID ,'')
FROM #TempFutureAppt)

它产生43,843个结果。我不确定我缺少什么。查询的逻辑是否不是“显示所有先前有约会但没有未来约会的患者”?

4 个答案:

答案 0 :(得分:1)

您的文字标准说

  

活跃的患者将是过去一年内预约的患者。

但是您的查询表明活动的患者是在过去一年内已预约的患者,或在将来的任何时间安排的患者:

  

WHERE Appointment_DateTime> DATEADD(year,-1,GETDATE())

以下查询假定为文本描述,而不是查询描述。鉴于此,EXISTS相关子查询应该可以让您到达想要的位置。

SELECT 
   Appt.Patient_ID
  ,Pat.Patient_Number
FROM 
  Appointments AS Appt
JOIN 
  Patients AS Pat 
    ON 
      Appt.Patient_ID = Pat.Patient_ID
WHERE 
  Appt.Appointment_DateTime !< GETDATE()
  AND 
  EXISTS
    (
      SELECT 
        1
      FROM 
        Appointments AS a2
      WHERE
        a2.Patient_ID = Appt.Patient_ID
        AND
        a2.Appointment_DateTime >= DATEADD(year,-1,CAST(GETDATE() AS DATE))
        AND
        a2.Appointment_DateTime < CAST(GETDATE() AS DATE)
      AND 
        a2.Status = 'X' 
        OR 
        a2.Status = 'N'
    )

修改

对不起,我原本并不认为“肮脏”。

设置:

DECLARE @t TABLE
(
  Patient_ID INT,
  Appointment_DateTime DATETIME
);

INSERT @t
VALUES
  (1,'20181001'),
  (1,'20181115'),
  (2,'20171204'),
  (3,'20190101');
  • 患者1:过去的约会和将来的约会。所以我们不在乎。
  • 患者2:过去的约会,以后没有约会。我们在乎。
  • 患者3:仅用于将来的约会。我们不在乎。

查询去年预约的患者(请注意,结束日期为“ GETDATE()”,上面的查询中缺少该日期。可能不重要,不确定。)

SELECT
  Patient_ID
FROM
  @t AS appt
WHERE 
  appt.Appointment_DateTime >= DATEADD(YEAR,-1,CAST(GETDATE() AS DATE))
  AND
  appt.Appointment_DateTime < CAST(GETDATE() AS DATE)

结果:

+------------+
| Patient_ID |
+------------+
|          1 |
|          2 |
+------------+

查询要预约的患者

SELECT
  Patient_ID
FROM
  @t AS appt2
WHERE 
  appt2.Appointment_DateTime > CAST(GETDATE() AS DATE)

结果:

+------------+
| Patient_ID |
+------------+
|          1 |
|          3 |
+------------+

查询过去约会但没有未来约会的患者:

SELECT
  Patient_ID
FROM
  @t AS appt
WHERE 
  appt.Appointment_DateTime >= DATEADD(YEAR,-1,CAST(GETDATE() AS DATE))
  AND
  appt.Appointment_DateTime < CAST(GETDATE() AS DATE)
EXCEPT
SELECT
  Patient_ID
FROM
  @t AS appt2
WHERE 
  appt2.Appointment_DateTime > CAST(GETDATE() AS DATE)

结果:

+------------+
| Patient_ID |
+------------+
|          2 |
+------------+

答案 1 :(得分:0)

您不需要通过公用表表达式执行临时表,就可以完成相同的工作

with cte1 as
(
  SELECT Patient_ID 
  FROM Appointments
  WHERE year(Appointment_DateTime)= year(getdate())-1 -- will last year records
  AND Status = 'X' OR Status = 'N'

), cte2 as
(
  SELECT Appt.Patient_ID, Pat.Patient_Number
  FROM Appointments AS Appt
  JOIN Patients AS Pat ON Appt.Patient_ID = Pat.Patient_ID
  WHERE Year(Appt.Appointment_DateTime)>year(getdate())-1 -- will pick except last year
  AND Appt.Patient_ID NOT IN (SELECT Patient_ID FROM cte1)
) select * from cte2

使用左联接可以完成相同的任务

with cte1 as
    (
      SELECT Patient_ID 
      FROM Appointments
      WHERE Year(Appointment_DateTime)= year(getdate())-1 --this will pick last year data
      AND Status = 'X' OR Status = 'N'

    ), cte2 as
    (
      SELECT Appt.Patient_ID, Pat.Patient_Number
      FROM Appointments AS Appt
      JOIN Patients AS Pat ON Appt.Patient_ID = Pat.Patient_ID
      WHERE year(Appt.Appointment_DateTime) > year(getdate())-1

    ) select cte2.* 
      from cte2 left join cte1 on cte2.Patient_ID=cte1.Patient_ID
      where cte1.Patient_ID is null

但是从您的查询中我明白了为什么您没有获取数据WHERE Appt.Appointment_DateTime < GETDATE()的原因,您必须更改此条件

答案 2 :(得分:0)

当子查询返回的任何值为NOT IN时,

NULL具有异常的行为。在这种情况下,NOT IN返回完全没有行。我认为这就是您所看到的行为。

由于以下原因,我强烈建议始终使用NOT EXISTS而不是NOT IN

SELECT Appt.Patient_ID, Pat.Patient_Number
FROM Appointments Appt JOIN
     Patients Pat
     ON Appt.Patient_ID = Pat.Patient_ID
WHERE Appt.Appointment_DateTime !< GETDATE() AND
      NOT EXISTS (SELECT 1 FROM #TempTableG t WHERE t.Patient_ID = Appt.Patient_ID);

我还要指出,>=!<更为普遍。

答案 3 :(得分:0)

由于您的问题,请尝试使用LEFT JOIN,例如:

SELECT Appt.Patient_ID, Pat.Patient_Number
  FROM Appointments AS Appt
  LEFT JOIN (
      SELECT Patient_ID
      FROM Appointments
      WHERE Appointment_DateTime > DATEADD(year,-1,GETDATE())
      AND Status = 'X' OR Status = 'N'
      GROUP BY Patient_ID
  ) AS Pat 
  ON Appt.Patient_ID = Pat.Patient_ID
  WHERE Pat.Patient_ID IS NULL

如果您提供体样本数据,我可以测试代码。