分组依据包含非聚合列

时间:2016-11-04 00:28:04

标签: mysql sql

我试图计算具有相同邮政编码的表格中每行的平均值,并按照该邮政编码和年份对其进行分组。我试图运行以下查询

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条件?

5 个答案:

答案 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中包含longitudegroup 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;

这样可获得邮政编码行的latitudelongitude的平均值。这并不准确,但它可能并不比仅仅抓住任意值更糟糕。

答案 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;