使用GROUP BY从MySql DB获取第二个最高值

时间:2012-12-24 07:58:32

标签: mysql sql group-by

我有一张表tbl_patient,我希望获取每位患者的最后2次访问,以便比较患者病情是在改善还是有所降低。

tbl_patient

id | patient_ID | visit_ID | patient_result
1  |     1      |   1      |     5
2  |     2      |   1      |     6
3  |     2      |   3      |     7
4  |     1      |   2      |     3
5  |     2      |   3      |     2
6  |     1      |   3      |     9

我尝试了下面的查询来获取每位患者的最后一次就诊,

SELECT MAX(id), patient_result FROM `tbl_patient` GROUP BY `patient_ID`

现在我想通过查询获取每位患者的第二次访问,但它给了我错误 (#1242 - 子查询返回超过1行)

SELECT id, patient_result FROM `tbl_patient` WHERE id <(SELECT MAX(id) FROM `tbl_patient` GROUP BY `patient_ID`) GROUP BY `patient_ID`

我错了

6 个答案:

答案 0 :(得分:4)

select p1.patient_id, p2.maxid id1, max(p1.id) id2
from tbl_patient p1
join (select patient_id, max(id) maxid
      from tbl_patient
      group by patient_id) p2
on p1.patient_id = p2.patient_id and p1.id < p2.maxid
group by p1.patient_id

id11是上次访问的ID,id2是第二次访问的ID。

答案 1 :(得分:2)

您的第一个查询未获得最后一次访问,因为它提供结果5和6而不是2和9。 您可以尝试此查询:

SELECT patient_ID,visit_ID,patient_result
FROM tbl_patient
where id in (
    select max(id) 
    from tbl_patient
    GROUP BY patient_ID)
union
SELECT patient_ID,visit_ID,patient_result
FROM tbl_patient
where id in (
    select max(id) 
    from tbl_patient
    where id not in (
        select max(id) 
        from tbl_patient
        GROUP BY patient_ID)
    GROUP BY patient_ID)
order by 1,2

答案 2 :(得分:1)

SELECT id, patient_result FROM `tbl_patient` t1
JOIN (SELECT MAX(id) as max, patient_ID FROM `tbl_patient` GROUP BY `patient_ID`) t2 
      ON t1.patient_ID = t2.patient_ID
WHERE id <max GROUP BY t1.`patient_ID`

答案 3 :(得分:1)

有几种方法可以在单个SQL语句中返回指定的结果集。

不幸的是,大多数这些方法都会产生相当笨拙的陈述。

在处理大型集合时,更优雅的语句往往会带来较差(或无法忍受)的性能。而往往具有更好性能的陈述看起来更不优雅。

最常见的三种方法可以使用:

  • 相关子查询
  • 不平等加入(几乎是笛卡尔积)
  • 两次传递数据

这是一种使用MySQL用户变量对数据使用两次传递的方法,它基本上模拟了其他DBMS中可用的分析RANK() OVER(PARTITION ...)函数:


SELECT t.id
     , t.patient_id
     , t.visit_id
     , t.patient_result
  FROM (
         SELECT p.id
              , p.patient_id
              , p.visit_id
              , p.patient_result
              , @rn := if(@prev_patient_id = patient_id, @rn + 1, 1) AS rn
              , @prev_patient_id := patient_id AS prev_patient_id
           FROM tbl_patients p
           JOIN (SELECT @rn := 0, @prev_patient_id := NULL) i
          ORDER BY p.patient_id DESC, p.id DESC
       ) t
WHERE t.rn <= 2

请注意,这涉及内联视图,这意味着将对表中的所有数据进行传递以创建“派生表”。然后,外部查询将针对派生表运行。所以,这基本上是对数据的两次传递。

通过消除内联视图返回的patient_id列的重复值,可以稍微调整此查询以提高性能。但我如上所示,所以我们可以更好地了解正在发生的事情。

这种方法在大型集合上可能相当昂贵,但通常比其他一些方法更有效。

另请注意,如果该患者只存在一个patient_id值,则此查询将返回id的行;它不会将回报限制在那些至少有两排的患者身上。


也可以使用相关子查询获得等效的结果集:

SELECT t.id
     , t.patient_id
     , t.visit_id
     , t.patient_result
  FROM tbl_patients t
 WHERE ( SELECT COUNT(1) AS cnt
           FROM tbl_patients p
          WHERE p.patient_id = t.patient_id
            AND p.id >= t.id
       ) <= 2
 ORDER BY t.patient_id ASC, t.id ASC

请注意,这是使用“依赖子查询”,这基本上意味着对于从t返回的每一行,MySQL实际上是针对数据库运行另一个查询。因此,对于大型集合来说,这将非常昂贵(就经过的时间而言)。


另一种方法是,如果每位患者的id值相对较少,您可以通过不平等加入来实现:

SELECT t.id
     , t.patient_id
     , t.visit_id
     , t.patient_result
  FROM tbl_patients t
  LEFT
  JOIN tbl_patients p
    ON p.patient_id = t.patient_id 
   AND t.id < p.id
 GROUP
    BY t.id
     , t.patient_id
     , t.visit_id
     , t.patient_result
HAVING COUNT(1) <= 2

请注意,这将为每位患者创建近乎笛卡尔的产品。对于每位患者的有限数量id值,这不会太糟糕。但如果患者有数百id个值,则中间结果可能很大,大约为(O)n ** 2.

答案 4 :(得分:0)

试试这个..

SELECT id, patient_result FROM tbl_patient AS tp WHERE id < ((SELECT MAX(id) FROM tbl_patient AS tp_max WHERE tp_max.patient_ID = tp.patient_ID)  - 1) GROUP BY patient_ID

答案 5 :(得分:-1)

为什么不简单地使用......

GROUP BY `patient_ID` DESC LIMIT 2

......并在下一步中完成其余的工作?