繁体   English   中英

SQL 通过选择可能为空的顶部行进行分组

[英]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 ):

  1. create_time可以是 null - 它应该被视为最小值
  2. group_id可以是 null - 应返回所有具有可为空group_id的行(如果不可能,我们可以使用coalesce(group_id, id)或类似假设 id 是唯一的并且永远不会与组 id 冲突)
  3. 应该可以对查询应用分页(所以连接可能是个问题)
  4. 查询应该尽可能通用(所以没有特定于供应商的东西)。 同样,如果不可能,它应该在 MySQL 5&8、PostgreSQL 9+ 和 H2 中工作

例如,预期的 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 支持,而其他的则有LIMITTOP子句。

如果这些微小的差异导致无法应用它们,那么您必须找到一种解决方法。 对于变量,这可以是一个包含最后读取的最大 ID 的单行表。 对于 fetch 子句,这可能意味着您只需获取所需的行数并停在那里。 当然这并不理想,因为 DBMS 并不知道您只需要接下来的 n 行,并且无法相应地优化执行计划。

然后可以选择不在 DBMS 中进行分页,而是将完整的结果读入您的应用程序并在那里处理分页(然后变成单纯的显示内容,当然会分配大量 memory)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM