Group by 包含非聚合列
Posted
技术标签:
【中文标题】Group by 包含非聚合列【英文标题】:Group by contains nonaggregated column 【发布时间】:2016-11-04 00:28:03 【问题描述】:我正在尝试计算具有相同邮政编码的表中每一行的平均值,并按该邮政编码和年份对其进行分组。我正在尝试运行以下查询
INSERT INTO processed_clean_properties (postcode,avgYearPostcodeNorm,latitude,longitude,yearSold)
SELECT postcode, round(avg(norm)), latitude, longitude, yearSold
FROM clean_properties
GROUP BY postcode, yearSold
并得到以下错误
" SELECT 列表的表达式 #3 不在 GROUP BY 子句中,并且包含在功能上不依赖于 GROUP BY 子句中的列的非聚合列 'forge.clean_properties.latitude';这与 sql_mode=only_full_group_by 不兼容"
我已经查看过它并尝试从 sql_mode 禁用 only_full_group_by,但如果服务器重新启动它似乎无法保存任何内容,它会重置为默认值。
我也尝试将所有选定的列按条件添加到组中
INSERT INTO processed_clean_properties (postcode,avgYearPostcodeNorm,latitude,longitude,yearSold)
SELECT postcode, round(avg(norm)), latitude, longitude, yearSold
FROM clean_properties
GROUP BY postcode, norm, latitude, longitude, yearSold
这样做会使查询无限期地运行而无需实际执行任何操作。
如何更正初始查询以使用 full_group_by 条件?
【问题讨论】:
错误信息告诉你,你正在做一些奇怪的事情。我倾向于同意。 我有一个表格,每行包含一个邮政编码、纬度、经度、价格、标准化价格和年份。我正在尝试以每个邮政编码按当年的平均标准化价格分组的方式将数据插入到新表中。 问题是,如果latitude
和longitude
在功能上依赖于postcode
。如果没有,您的查询没有意义。如果是,latitude
和 longitude
根本不应该在该表中。
【参考方案1】:
来自文档:
要告诉 mysql 接受查询,可以使用
ANY_VALUE()
函数。https://dev.mysql.com/doc/refman/5.7/en/group-by-handling.html
将ANY_VALUE()
添加到非聚合列。例如,ANY_VALUE(latitude) AS latitude
。
您遇到了这样一个事实,即在 MySQL 5.7 中,一项旧的优化——允许服务器不确定地返回非聚合列的每一组中的一行中的任何一个值——默认情况下不再有效。旧的优化组在技术上不是有效的 SQL——即使正确使用,它也是一个巨大的性能赢家。使用ANY_VALUE()
启用旧行为,同时明确表明您要求服务器相信您知道您在做什么,这些列在功能上确实依赖于 group by,因此从group 很好,因为应该都是一样的。
当然,如果它们在每个组中不完全相同,那么您的查询在逻辑上就有缺陷。
【讨论】:
优秀的答案!解释了我在寻找什么,谢谢!【参考方案2】:我认为您需要做的就是从您的 GROUP BY 中删除聚合列 norm
:
INSERT INTO processed_clean_properties (postcode,avgYearPostcodeNorm,latitude,longitude,yearSold)
SELECT postcode, round(avg(norm)), latitude, longitude, yearSold
FROM clean_properties
GROUP BY postcode, latitude, longitude, yearSold
如果这仍然永远运行,那只是意味着按所有这些附加列进行分组需要更长的时间。要解决这个问题,您需要告诉我们更多关于表结构的信息,更重要的是,发布解释计划。
另一个选项,要永久关闭only_full_group_by
,您需要在 my.cnf 文件中进行设置。此文件包含在服务器启动期间使用的配置。
【讨论】:
好吧,我删除它并再次运行查询并报告回来。谢谢 查询已执行但未产生预期结果。我正在尝试以一种方式对行进行分组,即每年只有 1 个具有平均标准的邮政编码。 @Barrera 那样的话,经纬度应该是多少?平均?按纬度和经度分组显然会为每个邮政编码提供许多记录。你可以看我回答的第二部分,或者在戈登的回答中使用经纬度的平均值。 每个邮政编码代表一个唯一的纬度和经度。我不确定平均纬度和经度会达到什么水平? @Barrera - 如果每个邮政编码始终具有相同的纬度和经度,您将得到您想要的结果。显然不是这样。我相信相同的邮政编码可以有多个纬度和经度,尽管它们都“靠近”。这就是为什么平均而言,最大值或最小值不会对纬度和经度产生太大影响,因此它们可能可以安全使用。【参考方案3】:如果在group by
中包含latitude
和longitude
会导致查询永远运行,那么这可能会产生相同的效果:
INSERT INTO processed_clean_properties (postcode, avgYearPostcodeNorm, latitude, longitude, yearSold)
SELECT postcode, round(avg(norm)),
avg(latitude), avg(longitude), yearSold
FROM clean_properties
GROUP BY postcode, yearSold;
这将获取邮政编码行的 latitude
和 longitude
的平均值。这并不准确,但它可能并不比抓取任意值更糟糕。
【讨论】:
查询已执行但未产生预期结果。我正在尝试以一种方式对行进行分组,即每年都会有唯一的邮政编码,并且具有平均标准。 @Barrera。 . .首先,这应该完全按照您所说的进行 - 每年每个邮政编码一行。那你为什么要关心经纬度呢? 用平均纬度和经度执行了一段时间,但这似乎完全符合我的要求。非常感谢。【参考方案4】:第二次尝试几乎是正确的,只需从 group by 列表中删除 norm
字段,因为您确实使用了聚合函数。
INSERT INTO processed_clean_properties (postcode,avgYearPostcodeNorm,latitude,longitude,yearSold)
SELECT postcode, round(avg(norm)), latitude, longitude, yearSold FROM clean_properties GROUP BY postcode, latitude, longitude, yearSold
如果上面的查询还是很慢,那么就得考虑在group by的字段上加一个多列索引了。
【讨论】:
好吧,我删除它并再次运行查询并报告回来。谢谢 查询已执行但未产生预期结果。我正在尝试以一种方式对行进行分组,即每年只有 1 个邮政编码具有平均标准。 那么这就是你应该问的问题。【参考方案5】:您也可以考虑先获取分组,然后执行连接
SELECT cp.latitude, cp.longitude, x.postcode, x.avg_norm, x.yearSold
FROM clean_properties cp JOIN (
SELECT postcode, round(avg(norm)) as avg_norm, yearSold
FROM clean_properties
GROUP BY postcode, yearSold ) x ON cp.postcode = x.postcode;
【讨论】:
以上是关于Group by 包含非聚合列的主要内容,如果未能解决你的问题,请参考以下文章
MySQL 5.0.12 - 列表不在 GROUP BY 子句中并且包含非聚合列?
ORDER BY 子句的表达式 #1 不在 GROUP BY 子句中,并且包含非聚合列
#1055 - SELECT 列表的表达式不在 GROUP BY 子句中,并且包含非聚合列,这与 sql_mode=only_full_group_by 不兼容