有几种方法可以在单个 SQL 语句中返回指定的结果集。
不幸的是,这些方法中的大多数都会产生相当笨拙的语句。
在处理大型集合时,看起来更优雅的语句往往表现不佳(或无法忍受)。往往具有更好性能的语句看起来更不优雅。
三种最常见的方法使用:
- 相关子查询
- 不等式连接(几乎是笛卡尔积)
- 两次遍历数据
这是一种使用 MySQL 用户变量对数据进行两次传递的方法,它基本上模拟了RANK() OVER(PARTITION ...)
其他 DBMS 中可用的分析函数:
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
存在一个值,则此查询将为 a 返回一行;它不仅限于返回至少有两排的患者。
也可以使用相关的子查询获得等效的结果集:
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
每个患者的值相对较少,则可以使用不等式 join来解决:
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。