我试图计算具有相同邮政编码的表格中每行的平均值,并按照该邮政编码和年份对其进行分组。我试图运行以下查询
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子句中,并且包含非聚合列' forge.clean_properties.latitude'它在功能上不依赖于GROUP BY子句中的列;这与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条件?
答案 0 :(得分:5)
来自文档:
要告诉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()
启用旧行为,同时明确表示您要求服务器信任您知道您正在做什么,这些列确实在功能上依赖于组,所以返回该组中的任何值都可以,因为它们都应该是相同的。
如果它们在每个组中都不完全相同,那么您的查询在逻辑上是有缺陷的。
答案 1 :(得分: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文件中设置它。此文件包含服务器启动期间使用的配置。
答案 2 :(得分:1)
第二次尝试几乎是正确的,只需从列表中删除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
如果上述查询仍然很慢,那么您必须考虑在组中的字段上添加多列索引。
答案 3 :(得分:1)
如果latitude
中包含longitude
和group by
会导致查询永久运行,那么这可能会产生相同的效果:
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
的平均值。这并不准确,但它可能并不比仅仅抓住任意值更糟糕。
答案 4 :(得分:0)
您还可以考虑先进行分组,然后再进行连接
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;