MySQL 按空值和非空值分组

Posted

技术标签:

【中文标题】MySQL 按空值和非空值分组【英文标题】:MySQL group by null and not null values 【发布时间】:2021-09-03 12:09:43 【问题描述】:

我有一张这样的桌子:

id | cluster_id | user_id | name      | ...
1  | 1          | 1       | test name
2  | 1          | 3       | other
3  | null       | 1       | one more
4  | 2          | 1       | foo
5  | null       | 1       | bar
6  | 1          | 1       | baz

我想创建一个按cluster_id 列分组的查询,但只按具有非空值的列分组,这样我就得到了这样的结果:

id | cluster_id | user_id | ...
1  | 1          | 1       | test name
3  | null       | 1       | one more
4  | 2          | 1       | foo
5  | null       | 1       | bar

我想要一个具有不同 cluster_id 的列表,但仅限于 cluster_id 不为空的地方。我还想过滤任意列,例如user_id

在上述结果中,我还查询了user_id,其中user_id 为1。

如何创建这样的查询?

提前致谢!

【问题讨论】:

这看起来一样,你可以创建一个minimal reproducible example 以便我们了解你想要达到的目标 @nbk 我已经更新了问题。我希望现在应该更清楚了:) 嗯,the columns that have a not null value 并且您的示例目标表中有一个空值 - 不太确定您要在那里实现什么。 @LukeBriggs 已修复 【参考方案1】:

查询很简单。

GROUP BY 也适用于 NULL 值

我做了两个查询,第一个包含 user_id 最后一个不包含

你必须用id作为主键来测试这个,看看排除NULL是否会带来一些性能

CREATE TABLE tab1 (
  `id` INTEGER,
  `cluster_id` int,
  `user_id` INTEGER,
  `name` VARCHAR(20)
);
INSERT INTO tab1
  (`id`, `cluster_id`, `user_id`, `name`)
VALUES
  ('1', '1', '1', 'test name'),
  ('2', '1', '3', 'other'),
  ('3', null, '1', 'one more'),
  ('4', '2', '1', 'foo'),
  ('5', null, '1', 'bar'),
  ('6', '1', '1', 'baz');
SELECT * FROM  tab1 WHERE `id` IN (SELECT MIN(`id`) FROM tab1 GROUP BY `cluster_id`,`user_id`)
UNION 
SELECT * FROM tab1 WHERE `cluster_id` IS NULL
编号 | cluster_id |用户 ID |姓名 -: | ---------: | ------: | :-------- 1 | 1 | 1 |测试名称 2 | 1 | 3 |其他 3 | | 1 |多一个 4 | 2 | 1 |富 5 | | 1 |酒吧
SELECT * FROM  tab1 WHERE `id` IN (SELECT MIN(`id`) FROM tab1 WHERE `cluster_id` IS NOT NULL GROUP BY `cluster_id`,`user_id`)
UNION 
SELECT * FROM tab1 WHERE `cluster_id` IS NULL
编号 | cluster_id |用户 ID |姓名 -: | ---------: | ------: | :-------- 1 | 1 | 1 |测试名称 2 | 1 | 3 |其他 4 | 2 | 1 |富 3 | | 1 |多一个 5 | | 1 |酒吧
SELECT * FROM  tab1 WHERE `id` IN (SELECT MIN(`id`) FROM tab1 GROUP BY `cluster_id`)
UNION 
SELECT * FROM tab1 WHERE `cluster_id` IS NULL
编号 | cluster_id |用户 ID |姓名 -: | ---------: | ------: | :-------- 1 | 1 | 1 |测试名称 3 | | 1 |多一个 4 | 2 | 1 |富 5 | | 1 |酒吧
SELECT * FROM  tab1 WHERE `id` IN (SELECT MIN(`id`) FROM tab1 WHERE `cluster_id` IS NOT NULL GROUP BY `cluster_id`)
UNION 
SELECT * FROM tab1 WHERE `cluster_id` IS NULL
编号 | cluster_id |用户 ID |姓名 -: | ---------: | ------: | :-------- 1 | 1 | 1 |测试名称 4 | 2 | 1 |富 3 | | 1 |多一个 5 | | 1 |酒吧

db小提琴here

【讨论】:

cluster_idnull 时,不应将其分组,仅对具有非空值的记录进行分组。在这种情况下,这将导致 2 个空记录。 我错过了那部分。 mi改了我的答案,你必须测试哪个更快 感谢您的回答。我遇到的问题是,每当我想过滤掉特定用户的结果时,查询总是会返回带有 cluster_id 的记录结果。如果我在 user_id=3 上查询,查询总是返回记录,即使不存在具有该 ID 的记录。 就是基本的sql add AND user_id = 3 after cluster_id IS NULLAND cluster_id IS NOT NULL 你要明白union是结合了两个独立的查询结构相同的,所以你过滤了之前的UNION或者之后

以上是关于MySQL 按空值和非空值分组的主要内容,如果未能解决你的问题,请参考以下文章

mysql创建表时的空值和非空值设置有啥讲究

Python 空值和非空值

从 Pyspark 中的数据框中计算空值和非空值

数据库数据插入,空值和非空判断,自动排序,约束主键,唯一约束,外健约束

使用 MySQL 按空值进行分区

MySQL在分组后获得第一个非空值