在 SQL 查询中选择最新或最近的日期

Posted

技术标签:

【中文标题】在 SQL 查询中选择最新或最近的日期【英文标题】:Select Latest or most recent date in SQL query 【发布时间】:2017-10-12 19:50:33 【问题描述】:

我正在我们的 EHR/EMR 数据库上运行 SQL 查询。我主要查看护士在每次患者就诊/就诊期间所做的评估,并希望返回最近评估日期的答案以及其他一些信息。我创建了查询并且所有数据都过来了,但是,它返回所有评估日期和答案,而不仅仅是最新的日期和答案。我将在下面附上完整的代码。

SELECT DISTINCT 
    MAX(PTA.ASSESSMENT_DATE) AS Max_Date, 
    SAQ.QUESTION_TEXT, SAA.ANSWER_TEXT, dbo.PT_BASIC.PATIENT_CODE,  
    dbo.PT_BASIC.NAME_FULL
FROM
    dbo.PTC_ASSESSMENT_ANSWER AS PAA 
INNER JOIN
    dbo.PTC_ASSESSMENT AS PTA ON PTA.ASSESSMENT_ID = PAA.ASSESSMENT_ID 
                              AND PTA.PATIENT_ID = PAA.PATIENT_ID 
INNER JOIN
    dbo.SYS_ASSESSMENT_POINTER AS SAP ON SAP.POINTER_ID = PAA.POINTER_ID   
INNER JOIN
    dbo.SYS_ASSESSMENT_QUESTION AS SAQ ON SAQ.QUESTION_ID = SAP.QUESTION_ID 
INNER JOIN
    dbo.SYS_ASSESSMENT_ANSWER AS SAA ON SAA.ANSWER_ID = SAP.ANSWER_ID 
INNER JOIN
    dbo.PT_BASIC ON PTA.PATIENT_ID = dbo.PT_BASIC.PATIENT_ID
WHERE
    (PTA.ASSESSMENT_DATE BETWEEN CONVERT(DATETIME, '2017-09-05 00:00:00', 102) 
                             AND CONVERT(DATETIME, '2017-10-12 00:00:00', 102))
GROUP BY 
    dbo.PT_BASIC.PATIENT_CODE, dbo.PT_BASIC.NAME_FULL, SAQ.QUESTION_TEXT, 
    SAA.ANSWER_TEXT
HAVING      
    (SAA.ANSWER_TEXT LIKE '%LEVEL % -%')

当前输出将类似于以下内容: 2017 年 9 月 5 日患者 ABC 答案1 2017 年 9 月 6 日患者 ABC 答案2 2017 年 9 月 7 日患者 ABC 答案3 2017 年 9 月 6 日患者 XYZ 回答4

我期待的是:

2017 年 9 月 7 日患者 ABC 答案3 2017 年 9 月 6 日患者 XYZ 回答4

【问题讨论】:

Retrieving last record in each group from database - SQL Server 2005/2008的可能重复 交叉应用而不是内部连接。 什么 RDBMS 和版本? PTC_Assessments 对答案的分布是什么?每个答案都有很多评估吗?或者只是几个,分布会随着时间的推移而变化吗? GROUP BY 生成唯一定义的行,也不需要select distinct(在单个选择查询中)。 【参考方案1】:

如果您的 SQL Server 版本支持它,使用ROW_NUMBER() OVER() 是一种从单个表中获取“最新”(或“最早”)行的有效且简单的方法。但是,由于我们对您的数据模型知之甚少,因此很难猜测如何将行减少到“最新答案”,这可能需要更复杂的子查询。但是,您仍然可以在该子查询上使用 ROW_NUMBER() OVER()。我怀疑问答的本质是表别名SAP、SAQ、SAA可能都需要参与到这个子查询中。

请注意,现在不是直接加入 PTA,而是一个子查询,并且外部查询的加入条件要求 RN=1 是具有“最新”日期的行。

SELECT
      MAX(PTA.ASSESSMENT_DATE) AS Max_Date
    , SAQ.QUESTION_TEXT
    , SAA.ANSWER_TEXT
    , dbo.PT_BASIC.PATIENT_CODE
    , dbo.PT_BASIC.NAME_FULL
FROM dbo.PTC_ASSESSMENT_ANSWER AS PAA
INNER JOIN (
      SELECT
            *
          , ROW_NUMBER() OVER (PARTITION BY PATIENT_ID
                               ORDER BY ASSESSMENT_DATE DESC) AS RN
      FROM dbo.PTC_ASSESSMENT
      WHERE ASSESSMENT_DATE BETWEEN  '20170905' AND '20171012'
      ) AS PTA ON PTA.ASSESSMENT_ID = PAA.ASSESSMENT_ID
            AND PTA.PATIENT_ID = PAA.PATIENT_ID
            AND PTA.RN = 1
INNER JOIN dbo.SYS_ASSESSMENT_POINTER AS SAP ON SAP.POINTER_ID = PAA.POINTER_ID
INNER JOIN dbo.SYS_ASSESSMENT_QUESTION AS SAQ ON SAQ.QUESTION_ID = SAP.QUESTION_ID
INNER JOIN dbo.SYS_ASSESSMENT_ANSWER AS SAA ON SAA.ANSWER_ID = SAP.ANSWER_ID
INNER JOIN dbo.PT_BASIC ON PTA.PATIENT_ID = dbo.PT_BASIC.PATIENT_ID
WHERE SAA.ANSWER_TEXT LIKE '%LEVEL % -%'
GROUP BY
      dbo.PT_BASIC.PATIENT_CODE
    , dbo.PT_BASIC.NAME_FULL
    , SAQ.QUESTION_TEXT
    , SAA.ANSWER_TEXT
    此查询不需要select distinct(或使用GROUP BY 的任何类似查询) yyymmdd 是 SQL Server 中最安全的日期文字,您不需要使用样式 102 进行转换 您的 having 子句应移至 where 子句,因为它不评估任何聚合值

【讨论】:

【参考方案2】:

交叉应用允许您使用相关查询并为每个患者评估提供按日期降序排列的前 n 条记录。 (经过审查,也许您只需要耐心?)

也许只是改变:

INNER JOIN
    dbo.PTC_ASSESSMENT AS PTA ON PTA.ASSESSMENT_ID = PAA.ASSESSMENT_ID 
                              AND PTA.PATIENT_ID = PAA.PATIENT_ID 

到:

CROSS APPLY (SELECT TOP 1 * 
             FROM dbo.PTC_ASSESSMENT PTA2 
             WHERE PTA2.ASSESSMENT_ID = PAA.ASSESSMENT_ID 
            /*AND PTA2.PATIENT_ID = PAA.PATIENT_ID*/
             ORDER BY PTA2.Assessment_date desc) PTA

给你:(我留下了 /AND PTA2.PATIENT_ID = PAA.PATIENT_ID/ --我认为你可以省略这个。我把 */ 留在原处,但不需要)

SELECT MAX(PTA.ASSESSMENT_DATE) AS Max_Date
     , SAQ.QUESTION_TEXT
     , SAA.ANSWER_TEXT
     , dbo.PT_BASIC.PATIENT_CODE
     , dbo.PT_BASIC.NAME_FULL
FROM dbo.PTC_ASSESSMENT_ANSWER AS PAA 
CROSS APPLY (SELECT TOP 1 * 
             FROM dbo.PTC_ASSESSMENT PTA2 
             WHERE PTA2.ASSESSMENT_ID = PAA.ASSESSMENT_ID   --I think you can omit this.
               /*AND PTA2.PATIENT_ID = PAA.PATIENT_ID*/
             ORDER BY PTA2.Assessment_date desc) PTA
INNER JOIN dbo.SYS_ASSESSMENT_POINTER AS SAP 
   ON SAP.POINTER_ID = PAA.POINTER_ID   
INNER JOIN dbo.SYS_ASSESSMENT_QUESTION AS SAQ 
   ON SAQ.QUESTION_ID = SAP.QUESTION_ID 
INNER JOIN dbo.SYS_ASSESSMENT_ANSWER AS SAA 
   ON SAA.ANSWER_ID = SAP.ANSWER_ID 
INNER JOIN dbo.PT_BASIC 
   ON PTA.PATIENT_ID = dbo.PT_BASIC.PATIENT_ID
WHERE (PTA.ASSESSMENT_DATE BETWEEN CONVERT(DATETIME, '2017-09-05 00:00:00', 102) AND CONVERT(DATETIME, '2017-10-12 00:00:00', 102))
GROUP BY dbo.PT_BASIC.PATIENT_CODE
       , dbo.PT_BASIC.NAME_FULL
       , SAQ.QUESTION_TEXT
       , SAA.ANSWER_TEXT
HAVING (SAA.ANSWER_TEXT LIKE '%LEVEL % -%')

您似乎并不关心没有评估的患者,因为您的所有联接都是内部联接,或者我们可以使用外部应用来确保保留所有答案,无论是否提供了评估。

或者,您可以使用 row_number() 逻辑(Tab Alleman 的链接已涵盖此内容)和 cte;但是如果可以使用交叉应用,不妨在这里使用它。

【讨论】:

我进行了这些更改,现在结果更好,但如果患者在不同的一天有 2 个不同的答案,我会同时列出这两个答案。例如:9/6/17 Patient1 Answer1、9/7/17 Patient1 Answer2。我没有得到每一个评估答案,而只是那些现在我真正需要的是最新答案的独特答案。 然后我认为将assessment_ID作为相关查询的一部分消除。我已经在我认为您可以在上面的答案中删除的部分周围放置了评论块 /* */ 我想越来越近了。最大日期字段现在显示最新的评估日期,但我得到了几个不同的答案。对于 Patient1,我得到的是 Answer1、Answer2 和 Answer3,但所有 3 个答案的日期都显示为最新日期,尽管所有 3 个答案都在不同的日期。【参考方案3】:

请添加order by PTA.ASSESSMENT_DATE DESC以在顶部查看最新记录。

【讨论】:

以上是关于在 SQL 查询中选择最新或最近的日期的主要内容,如果未能解决你的问题,请参考以下文章

SQL 选择除最近日期之外的所有内容

在Oracle中选择具有最近日期和时间的记录[重复]

sql db2 选择每个员工的最新条目及其以前的条目

ORACLE 中的 SQL 查询 - 选择最近的日期

有关给定日期的sql查询,如果不存在,请提供最新日期

从日期中选择特定记录的 SQL 查询