MySQL - 选择不在Group By中的列

时间:2009-06-21 07:06:59

标签: mysql group-by

我正在尝试向预先存在的应用程序添加功能,我遇到了类似这样的MySQL视图:

SELECT
     AVG(table_name.col1),
     AVG(table_name.col2),
     AVG(table_name.col3),
     table_name.personID,
     table_name.col4
FROM table_name
GROUP BY table_name.personID;

好的,所以有一些聚合函数。您可以选择personID,因为您正在对其进行分组。但它也是选择一个不在聚合函数中的列,而不是GROUP BY子句的一部分。这怎么可能???它只是选择一个随机值,因为每个组的值绝对不是唯一的吗?

我来自哪里(MSSQL Server),这是一个错误。有人可以向我解释这种行为以及为什么它在MySQL中被允许吗?

4 个答案:

答案 0 :(得分:47)

这个功能确实允许一些不明确的查询,并以一个从该列中选取的任意值静默返回结果集。实际上,它往往是首先物理存储的组中行的值。

如果您只选择功能上依赖于GROUP BY条件中的列的列,则这些查询不会有歧义。换句话说,如果每个定义组的值只有一个“模糊”列的不同值,则没有问题。此查询在Microsoft SQL Server(和ANSI SQL)中是非法的,即使它在逻辑上不会导致歧义:

SELECT AVG(table1.col1), table1.personID, persons.col4
FROM table1 JOIN persons ON (table1.personID = persons.id)
GROUP BY table1.personID;

此外,MySQL有一种SQL模式,使其符合标准:ONLY_FULL_GROUP_BY

FWIW,SQLite也允许这些不明确的GROUP BY子句,但它从组中的 last 行中选择值。


至少在我测试过的版本中。 任意意味着MySQL或SQLite将来可能会改变它们的实现,并且会有一些不同的行为。因此,您不应该依赖于他们目前处于模糊情况的行为。最好将您的查询重写为确定性而不是模糊不清。这就是MySQL 5.7现在默认启用ONLY_FULL_GROUP_BY的原因。

答案 1 :(得分:11)

我应该用Google搜索一段时间......似乎我找到了my answer

  

MySQL扩展了GROUP BY的使用范围   您可以使用非聚合列   或SELECT列表中的计算   没有出现在GROUP BY中   条款。您可以使用此功能   通过避免获得更好的性能   不必要的列排序和   分组。例如,您不需要   在...中的customer.name分组   以下查询

     

在标准SQL中,您必须添加   customer.name到GROUP BY子句。   在MySQL中,名称是多余的。

但是,这似乎......错了。

答案 2 :(得分:0)

select * from personel where p_id IN(select
min(dbo.personel.p_id)
FROM
personel
GROUP BY dbo.personel.p_adi)

答案 3 :(得分:0)

假设您有这样的查询:

SELECT g, v 
FROM t
GROUP BY g;

在这种情况下,对于g的每个可能值,mysql会选择v的相应值之一。

但是,选择哪种取决于具体情况。

我在某处读到,对于每组g,按照将记录插入表v的顺序保留t的第一个值。

这很丑陋,因为表中的记录应被视为 set ,其中元素的顺序无关紧要。真是“ mysql-ish” ...

如果要确定要保留v的哪个值,则需要为t应用子选择,如下所示:

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        ORDER BY g, v DESC
) q
GROUP BY g;

这样,您可以定义子查询的记录由外部查询处理的顺序,因此您可以信任它将为v的各个值选择哪个值g

但是,如果您需要一些WHERE条件,请非常小心。如果将WHERE条件添加到子查询中,则它将保留该行为,它将始终返回您期望的值:

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
GROUP BY g;

这是您所期望的,子选择项过滤并排序表。它保留记录g具有给定值,并且外部查询返回该gv的第一个值。

但是,如果将相同的WHERE条件添加到外部查询,则会得到不确定的结果:

SELECT g, v 
FROM (
    SELECT * 
        FROM t 
        -- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
GROUP BY g;

令人惊讶的是,当一次又一次地执行相同的查询时,v可能会获得不同的值,这很奇怪。预期的行为是从子查询中以适当的顺序获取所有记录,在外部查询中对其进行过滤,然后选择与上一个示例中相同的记录。但事实并非如此。

它似乎是随机选择v的值。如果执行更多(〜20)次,但分布不均匀,则同一查询将为v返回不同的值。

如果您不添加外部WHERE,而是指定如下所示的HAVING条件:

SELECT g, v 
FROM (
    SELECT * 
        FROM t1 
        -- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9' 
        ORDER BY g, v DESC
) q
-- WHERE g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9'
GROUP BY g
HAVING g = '737a8783-110c-447e-b4c2-1cbb7c6b72c9';

然后您将再次获得一致的行为。

结论:我建议完全不要依赖此技术。如果您确实希望/需要避免在外部查询中使用WHERE条件。如果可以,请在内部查询中使用它,或者在外部查询中使用HAVING子句。

我用以下数据进行了测试:

CREATE TABLE t1 (
    v INT,
    g VARCHAR(36)
);

INSERT INTO t1 VALUES (1, '737a8783-110c-447e-b4c2-1cbb7c6b72c9');
INSERT INTO t1 VALUES (2, '737a8783-110c-447e-b4c2-1cbb7c6b72c9');
在mysql 5.6.41中为

也许这只是在新版本中得到修复的错误,如果您有使用新版本的经验,请提供反馈。