59

我正在尝试向预先存在的应用程序添加功能,我遇到了一个类似这样的 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

4 回答 4

59

确实,此功能允许一些模棱两可的查询,并默默地返回一个结果集,该结果集具有从该列中选择的任意值。在实践中,它往往是首先物理存储的组内行的值。

如果您只选择在功能上依赖于 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 子句,但它选择组中最后一行的值。†</sup>


†</sup> 至少在我测试的版本中。任意的意思是 MySQL 或 SQLite 将来可能会改变它们的实现,并有一些不同的行为。因此,在这种模棱两可的情况下,您不应依赖当前的行为方式。最好将您的查询重写为确定性而不是模棱两可。这就是 MySQL 5.7 现在默认启用 ONLY_FULL_GROUP_BY 的原因。

于 2009-06-21T08:22:26.543 回答
16

我应该再用 Google 搜索一下……看来我找到了答案

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

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

不过,这似乎……是错误的。

于 2009-06-21T07:09:40.807 回答
2

假设您有这样的查询:

SELECT g, v 
FROM t
GROUP BY g;

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

但是,选择哪一个取决于某些情况。

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

这很丑陋,因为表中的记录应该被视为一个集合,其中元素的顺序不重要。这太“mysql-ish”了……

如果要确定v要保留哪个值,则需要t像这样应用子选择:

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

通过这种方式,您可以定义外部查询处理子查询记录的顺序,因此您可以相信它将vg.

但是,如果您需要一些 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具有给定值的记录,外部查询返回该g值和v.

但是,如果将相同的 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看似随机的值。v如果我执行更多(~20)次但分布不均匀,相同的查询返回不同的值。

如果不是添加外部 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 中。

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

于 2018-12-05T05:33:28.643 回答
0
select * from personel where p_id IN(select
min(dbo.personel.p_id)
FROM
personel
GROUP BY dbo.personel.p_adi)
于 2013-04-15T09:21:45.020 回答