MySQL-选择不在分组依据中的列

pgvzfuti  于 2022-10-03  发布在  Mysql
关注(0)|答案(4)|浏览(212)

我正试图向一个已有的应用程序添加功能,我遇到了一个类似于以下内容的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中允许这种行为?

lymgl2op

lymgl2op1#

确实,该功能允许一些模棱两可的查询,并以静默方式返回一个结果集,该结果集包含从该列中选取的任意值。实际上,首先物理存储的往往是组内行中的值。

如果您只选择在功能上依赖于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子句,但它从GROUP中的LAST行选择值。†

†,至少在我测试的版本中是这样。所谓“任意”的意思是,MySQL或SQLite都可以在未来改变它们的实现,并有一些不同的行为。因此,在这种模棱两可的情况下,你不应该依赖于行为保持不变。最好将您的查询重写为确定性的,而不是模棱两可的。这就是为什么MySQL5.7现在默认只启用_FULL_GROUP_的原因。

ddarikpa

ddarikpa2#

我应该再搜索一段时间的……看来我找到了my answer
MySQL扩展了GROUP BY的使用范围,以便您可以在选择列表中使用GROUP BY子句中未出现的非聚集列或计算。您可以使用此功能通过避免不必要的列排序和分组来获得更好的性能。例如,您不需要在以下查询中对Customer.name进行分组

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

尽管如此,这似乎只是...不对。

t98cgbkg

t98cgbkg3#

假设您有一个如下所示的查询:

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;

通过这种方式,您可以定义子查询的记录被外部查询处理的顺序,因此您可以信任外部查询将为g的各个值选择v的哪个值。

但是,如果您需要一些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中。

也许这只是一个在新版本中得到修复的错误,如果你有使用新版本的经验,请给予反馈。

cygmwpex

cygmwpex4#

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

相关问题