[英]SQL group by selecting top rows with possible nulls
示例表:
ID | 姓名 | 创建时间 | group_id |
---|---|---|---|
1 | 一个 | 2022-01-01 12:00:00 | 组 1 |
2 | b | 2022-01-01 13:00:00 | 组 1 |
3 | c | 2022-01-01 12:00:00 | NULL |
4 | d | 2022-01-01 13:00:00 | NULL |
5 | e | NULL | 组2 |
我需要在以下条件下按group_id
分组的前 1 行(具有最小的create_time
):
create_time
可以是 null - 它应该被视为最小值group_id
可以是 null - 应返回所有具有可为空group_id
的行(如果不可能,我们可以使用coalesce(group_id, id)
或类似假设 id 是唯一的并且永远不会与组 id 冲突)例如,预期的 output:
ID | 姓名 | 创建时间 | group_id |
---|---|---|---|
1 | 一个 | 2022-01-01 12:00:00 | 组 1 |
3 | c | 2022-01-01 12:00:00 | NULL |
4 | d | 2022-01-01 13:00:00 | NULL |
5 | e | NULL | 组2 |
我已经阅读过关于 SO 的类似问题,但 90% 的答案都使用特定关键字(许多答案使用PARTITION BY
,例如https://stackoverflow.com/a/6841644/5572007 ),其他人不尊重组中的 null 值条件列和可能的分页(如https://stackoverflow.com/a/14346780/5572007 )。
我猜
SELECT id, name, MAX(create_time), group_id
FROM tb GROUP BY group_id
UNION ALL
SELECT id, name, create_time, group_id
FROM tb WHERE group_id IS NULL
ORDER BY name
我应该指出,“名称”是一个保留字。
select *
from T t1
where coalesce(create_time, 0) = (
select min(coalesce(create_time, 0)) from T t2
where coalesce(t2.group_id, t2.id) = coalesce(t1.group_id, t1.id)
)
您可以将两个查询与UNION ALL
结合起来。 例如:
select id, name, create_time, group_id
from mytable
where group_id is not null
and not exists
(
select null
from mytable older
where older.group_id = mytable.group_id
and older.create_time < mytable.create_time
)
union all
select id, name, create_time, group_id
from mytable
where group_id is null
order by id;
这是标准的 SQL 并且非常基本。 它应该适用于几乎每个 RDBMS。
至于分页:这通常代价高昂,因为您一次又一次地运行相同的查询,以便始终选择结果的“下一个”部分,而不是只运行一次查询。 最好的方法通常是使用主键进入下一部分,以便可以使用键上的索引。 在上面的查询中,我们最好将where id >:last_biggest_id
添加到查询中并限制结果,这将fetch next <n> rows only
。 每次我们运行查询时,我们使用最后读取的 ID 作为:last_biggest_id
,所以我们从那里继续读取。
然而,在不同的 DBMS 中,变量的处理方式不同。 最常见的是它们前面有一个冒号、一个美元符号或一个 at 符号。 标准的 fetch 子句也只有一些 DBMS 支持,而其他的则有LIMIT
或TOP
子句。
如果这些微小的差异导致无法应用它们,那么您必须找到一种解决方法。 对于变量,这可以是一个包含最后读取的最大 ID 的单行表。 对于 fetch 子句,这可能意味着您只需获取所需的行数并停在那里。 当然这并不理想,因为 DBMS 并不知道您只需要接下来的 n 行,并且无法相应地优化执行计划。
然后可以选择不在 DBMS 中进行分页,而是将完整的结果读入您的应用程序并在那里处理分页(然后变成单纯的显示内容,当然会分配大量 memory)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.