我正在尝试获取每个月特定学生健康检查的类别。然而,有时一个月会有多个条目。如何使用 SQL 获取当月的最新条目?
以下是我正在使用的SQL:
SELECT mt_tagged_student.student_id,
mt_inspection.category,
mt_inspection.created_date as entry_date
FROM public.mt_tagged_student
LEFT JOIN public.mt_student On mt_tagged_student.student_id = mt_student.id
LEFT JOIN public.mt_inspection On mt_inspection.student_id = mt_student.id
where mt_inspection.created_date between '2023-10-1 00:00:01' AND '2023-10-31 23:59:59'
and mt_inspection.category = 1
ORDER BY mt_tagged_student.student_id ASC, entry_date Desc
这是 SQL 的一些结果,
突出显示的部分显示有时一个月内有两次条目。如何只获取该月的最新条目?
DISTINCT ON
的教科书示例:
SELECT DISTINCT ON (s.student_id)
s.student_id
, i.category
, i.created_date AS entry_date
FROM public.mt_tagged_student t
JOIN public.mt_student s USING (student_id)
JOIN public.mt_inspection i USING (student_id)
WHERE i.created_date >= '2023-10-1'
AND i.created_date < '2023-11-1'
AND i.category = 1
ORDER BY s.student_id, i.created_date DESC NULLS LAST;
也应该像您指出的那样,每月最多只执行几个条目。参见:
另外,不要将
BETWEEN
与时间戳一起使用。
我将
LEFT [OUTER] JOIN
的两个实例都转换为普通 [INNER] JOIN
,因为前者与右表中的 WHERE
条件结合起来没有任何意义。参见:
我一开始就加入了
DESC NULLS LAST
,因为LEFT JOIN
可以引入空值。现在已删除,但我们仍然不知道 created_date
本身是否已定义 NOT NULL
。参见:
您可以按如下方式使用
NOT EXISTS
:
SELECT mt_tagged_student.student_id,
mi.category,
mi.created_date as entry_date
FROM public.mt_tagged_student
LEFT JOIN public.mt_student On mt_tagged_student.student_id = mt_student.id
LEFT JOIN public.mt_inspection mi On mi.student_id = mt_student.id
where mi.created_date between '2023-10-1 00:00:01' AND '2023-10-31 23:59:59'
and mi.category = 1
and not exists (select 1 from public.mt_inspection mi
where mi.student_id = mii.student_id
and date_trunc('month', mi.created_date) = date_trunc('month', mii.created_date)
and mii.created_date > mi.created_date
and mii.category = 1)
ORDER BY mt_tagged_student.student_id ASC, entry_date Desc
或者您可以使用
ROW_NUMBER
,如下所示:
select t.student_id, t.category, t.entry_date from
(SELECT mt_tagged_student.student_id,
mt_inspection.category,
mt_inspection.created_date as entry_date,
row_number() over (partition by mt_tagged_student.student_id, date_trunc('month', mt_inspection.created_date) order by mt_inspection.created_date desc) as rn
FROM public.mt_tagged_student
LEFT JOIN public.mt_student On mt_tagged_student.student_id = mt_student.id
LEFT JOIN public.mt_inspection On mt_inspection.student_id = mt_student.id
where mt_inspection.created_date between '2023-10-1 00:00:01' AND '2023-10-31 23:59:59'
and mt_inspection.category = 1) t
ORDER BY t.student_id ASC, t.entry_date Desc;
WITH ranked_inspection AS (
SELECT
mt_tagged_student.student_id,
mt_inspection.category,
mt_inspection.created_date AS entry_date,
ROW_NUMBER() OVER (PARTITION BY mt_tagged_student.student_id
ORDER BY mt_inspection.created_date DESC) AS rnk
FROM
public.mt_tagged_student
LEFT JOIN public.mt_student ON mt_tagged_student.student_id = mt_student.id
LEFT JOIN public.mt_inspection ON mt_inspection.student_id = mt_student.id
WHERE
mt_inspection.created_date >= '2023-10-1'
AND mt_inspection.created_date < '2023-11-1'
AND mt_inspection.category = 1
)
SELECT
student_id,
category,
entry_date
FROM
ranked_inspection
WHERE
rnk = 1
ORDER BY
student_id ASC, entry_date DESC;