题
我正在尝试向预先存在的应用程序添加功能,我遇到了类似这样的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中被允许吗?
解决方案
这个功能确实允许一些不明确的查询,并以一个从该列中选取的任意值静默返回结果集。实际上,它往往是首先物理存储的组中行的值。
如果您只选择功能上依赖于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的原因。
其他提示
我应该用谷歌搜索一下......似乎我找到了我的回答。
MySQL扩展了GROUP BY的使用范围 您可以使用非聚合列 或SELECT列表中的计算 没有出现在GROUP BY中 条款。您可以使用此功能 通过避免获得更好的性能 不必要的列排序和 分组。例如,您不需要 在...中的customer.name分组 以下查询
在标准SQL中,您必须添加 customer.name到GROUP BY子句。 在MySQL中,名称是多余的。
但是,这似乎......错了。
select * from personel where p_id IN(select
min(dbo.personel.p_id)
FROM
personel
GROUP BY dbo.personel.p_adi)
假设您有这样的查询:
SELECT g, v
FROM t
GROUP BY g;
在这种情况下,对于 g
的每个可能值,mysql选择 v
的相应值之一。
但是,选择哪一个取决于某些情况。
我在某处读到,对于每组g,将按照记录如何插入表 t
的顺序保留 v
的第一个值。
这非常难看,因为表中的记录应该被视为 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
具有给定值的记录,外部查询返回 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
的值。如果我执行了更多(~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中。
也许这只是一个在较新版本中得到/修复的错误,如果您有较新版本的经验,请提供反馈。