MySQL GroupBy在多列上的怪异行为

时间:2019-09-29 22:05:03

标签: mysql sql group-by greatest-n-per-group

我正在编写一个查询,该查询按(height, sex)对用户表进行分组,然后以heightsex的组合获得具有最小权重的用户名。

如果您想跳到实际的查询,这里是SQLFiddle,带有最小代表示例。

如果您不想打开该URL,这就是我在说的。给定一个表,例如:

| height | sex    | name    | weight |
|-------:|--------|---------|--------|
| 100    | female | Alice   | 150    |
| 100    | female | Barbara | 130    |
| 100    | female | Candice | 100    |

和执行此操作的查询,如下所示:

SELECT name, min(weight) from users
group by height, sex

查询为什么输出:

|  name | min(weight) |
|------:|-------------|
| Alice | 100         |

我真正想要的是Candice, 100而不是Alice, 100

我发现它选择Alice是因为它是第一行,但是为什么要这么做呢?真是奇怪又出乎意料。

3 个答案:

答案 0 :(得分:1)

您没有正确使用GROUP BY。在大多数RDBMS中,通常的规则是所有未聚合的列都必须出现在GROUP BY子句中。只有较早版本的MySQL才允许违反该规则(在较新的版本中,您需要禁用选项ONLY_FULL_GROUP_BY),但这会导致不可预测的结果,如您在此处所遇到的。

要展示共享相同的weightheight的推子组中sex最小的用户,一种解决方案是将NOT EXISTS条件与相关子查询,例如:

SELECT *
FROM users u
WHERE NOT EXISTS (
    SELECT 1
    FROM users u1
    WHERE 
        u1.height = u.height 
        AND u1.sex = u.sex
        AND u1.weight < u.weight
)

合理比例:NOT EXITS条件与子查询结合使用,可以消除存在相同heightsex但具有较小weight的另一个记录的记录,并保留在结果集仅对每个weight元组具有最小height/sex的记录。我们使用SELECT 1是因为我们不需要子查询来返回实际结果,我们只想知道它是否返回 something

答案 1 :(得分:1)

我将使用相关的子查询,但不能使用花哨的逻辑:

select u.*
from users u
where u.weight = (select min(u2.weight)
                  from users u2
                  where u2.height = u.height and u2.sex = u.sex
                 );

也就是说,返回权重为height / sex组合的最小值的用户。

答案 2 :(得分:0)

有了这些数据

CREATE TABLE users
(`height` varchar(8), `sex` varchar(8), `name` varchar(9), `weight` varchar(8))
;

INSERT INTO users
(`height`, `sex`, `name`, `weight`)
VALUES
('100', 'female', 'Alice', '150'),
('100', 'female', 'Barbara', '130'),
('100', 'female', 'Candice', '100'),
('190', 'male', 'John', '185'),
('190', 'male', 'Bert', '130'),
('190', 'male', 'John', '113')
;

此sql语句

Select name, u.weight
from users u inner join 
(Select min(weight) weight ,sex,height
From users
Group by height, sex ) u1 
On u.sex= u1.sex and u.height = u1.height
and u.weight = u1.weight
GROUP By u.height,u.sex;

您得到以下结果

name        weight
Candice     100
John        113

第二个选择语句u1使所有组的性别和身高最小化,其余是简单的内部联接,其中仅选择相关名称