在一对多关系中,根据MIN值返回不同的行

时间:2014-08-28 14:28:53

标签: sql postgresql one-to-many greatest-n-per-group

假设病人多次访问。我想编写一个查询,根据他们最早的访问返回不同的患者行。例如,请考虑以下行。

patients
-------------
id    name
1     Bob
2     Jim
3     Mary

visits
-------------
id    patient_id    visit_date    reference_number
1     1             6/29/14       09f3be26
2     1             7/8/14        34c23a9e
3     2             7/10/14       448dd90a

查询返回的内容是:

id    name    first_visit_date    reference_number
1     Bob     6/29/14             09f3be26
2     Jim     7/10/14             448dd90a

我尝试的内容类似于:

SELECT
  patients.id, 
  patients.name, 
  visits.visit_date AS first_visit_date, 
  visits.reference_number
FROM
  patients
INNER JOIN (
  SELECT
    *
  FROM
    visits
  ORDER BY
    visit_date
  LIMIT
    1
) visits ON
  visits.patient_id = patients.id

添加LIMIT会导致查询返回0行,但删除它会导致查询返回重复项。这里的诀窍是什么?我也尝试在INNER JOIN中选择MIN(visit_date),但也会返回重复项。

更新

有人建议这个问题是重复的,但对我而言似乎有所不同,因为我在两个单独的表中这样做。另一个问题上接受的答案建议加入y.max_total = x.total,如果正在加入的表是从中选择的表,则可以使用{{1}}。另外,我需要使用MIN日期返回行中的其他列,而不仅仅是日期本身。

然而,我接受的答案很有效。

4 个答案:

答案 0 :(得分:2)

避免使用DISTINCT ON(p.id),而是使用普通的NOT EXISTS(...)代替

SELECT p.id, p.name
     , v.first_visit_date, v.reference_number
FROM patients p
JOIN visits v ON p.id = v.patient_id
    -- exclude all join-products that are not the first for a patient.
WHERE NOT EXISTS (
   SELECT *
   FROM visits nx
   WHERE nx.patient_id = v.patient_id
   AND ( nx.visit_date < v.visit_date
       OR (nx.visit_date = v.visit_date AND nx.id < v.id) -- tie-breaker condition
       )
   );  

答案 1 :(得分:1)

使用distinct on

select distinct on (p.id)
    p.id, 
    p.name, 
    v.visit_date as first_visit_date, 
    v.reference_number
from
    patients p
    inner join
    visits v on p.id = v.patient_id
order by p.id, v.visit_date

http://www.postgresql.org/docs/current/static/sql-select.html#SQL-DISTINCT

答案 2 :(得分:0)

您希望聚合查询将visits表的多行减少为每个患者ID一行。 LIMIT有点乱,因为它不是严格的关系,但如果你有足够的决心,你可能会使它工作。如果查询优化器足够好,那么使用LIMIT(适当)的版本之间应该没有性能差异:

SELECT
  patients.id, 
  patients.name, 
  visits.first_visit_date AS first_visit_date, 
  visits.reference_number
FROM
  patients
INNER JOIN (
  SELECT
    patient_id,
    MIN(visit_date) as first_visit_date
  FROM
    visits
  GROUP BY
    patient_id
) visits ON
  visits.patient_id = patients.id

我更喜欢带有MIN()的版本,因为它对我来说更清晰。此外,如果它对我来说更清楚,那么它也更有可能让查询优化器更清晰。

答案 3 :(得分:0)

虽然您使用的是PostgreSQL,但为了防止它有用或“鼓舞人心”,这里是T-SQL版本。

SELECT p.id, name, first_visit.visit_date as first_visit_date, v.reference_number as first_visit_reference_number
FROM patients p
INNER JOIN 
(
    SELECT patient_id, MIN(visit_date) AS visit_date
    FROM visits
    GROUP BY patient_id
) first_visit ON first_visit.patient_id = p.id
INNER JOIN visits v ON v.patient_id = p.id AND v.visit_date = first_visit.visit_date
相关问题