自定义查询过滤器的 ORDER BY 与 GROUP BY 发生冲突。

想请问一下大佬们, 我有一个自定义的 Post Type,并希望按照 meta key 进行排序。我添加了以下代码,它是可以工作的:

function training_qv_anyx_training($query, $block) {
    if ( 'anyx-training' === $query['post_type']) {
         $query['meta_key'] = "anyx-training-order";
         $query['orderby'] = "meta_value_num";
         $query['order'] = "ASC";
    }
    return $query;
}
add_filter( 'query_loop_block_query_vars', 'training_qv_anyx_training', 10, 2 );

但是用这个方法的效果好像很不稳定,loop有时候会返回空结果。

使用 Query Monitor 调试时,我发现 WordPress 生成的 SQL 查询如下,并且它有报错 ORDER BY 相关的问题:

SELECT wp_posts.ID
FROM wp_posts
LEFT JOIN wp_term_relationships
ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_postmeta
ON ( wp_posts.ID = wp_postmeta.post_id )
WHERE 1=1
AND ( wp_term_relationships.term_taxonomy_id IN (1890041) )
AND ( wp_postmeta.meta_key = 'anyx-training-order' )
AND ((wp_posts.post_type = 'anyx-training'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'complete'
OR wp_posts.post_status = 'private')))
GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value ASC;

报错信息如下:

ERROR 1055 (42000): Expression #1 of ORDER BY is not in GROUP BY clause and contains nonaggregated column '' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by

想求助一下大佬们是不是我哪里做错了?

order by 用到的字段要包含在group by里。

1 个赞

wp_postmeta.meta_value不是GROUP BY字段,需要去掉sql_mode里面的ONLY_FULL_GROUP_BY设置,不过这样的sql一般也不合理

:thinking:不能去掉吧,去掉的话,结果应该不一定是对的。
:yum:在我司,这些写法就是开发不规范,要重新做的。

SQL 语义不明,不符合sql_mode ONLY_FULL_GROUP_BY,数据按 wp_posts.ID 聚合,如何还能按 wp_postmeta.meta_value 排序?聚合后的结果并没有 meta_value 列啊

这个错误信息 ERROR 1055 (42000): Expression #1 of ORDER BY is not in GROUP BY clause and contains nonaggregated column '' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by 是 MySQL 在启用 ONLY_FULL_GROUP_BY SQL 模式时抛出的,下面为你详细解释该错误的含义和产生原因,以及解决办法。

错误含义

在 MySQL 开启 ONLY_FULL_GROUP_BY 模式时,SELECTHAVING 或者 ORDER BY 子句里所使用的列,必须满足以下两个条件之一,否则就会报错:

  1. 该列必须包含在 GROUP BY 子句中。
  2. 该列必须使用了聚合函数(例如 SUM()COUNT()AVG() 等)。

产生原因

你提供的 SQL 语句中,ORDER BY 子句使用了 wp_postmeta.meta_value 列,然而这个列既没有出现在 GROUP BY 子句中,也没有使用聚合函数进行处理。这就违背了 ONLY_FULL_GROUP_BY 模式的规则,因为 wp_postmeta 表中可能存在多条与 wp_posts.ID 关联的记录,每条记录的 meta_value 可能不同,所以 MySQL 无法明确按照哪个 meta_value 进行排序。

解决办法

1. 修改查询语句

使用聚合函数来处理 ORDER BY 子句中的列,确保其符合 ONLY_FULL_GROUP_BY 模式的要求。例如,使用 MIN() 或者 MAX() 函数来获取 meta_value 的最小值或者最大值:

收起

sql

SELECT wp_posts.ID, MIN(wp_postmeta.meta_value) AS min_meta_value
FROM wp_posts
LEFT JOIN wp_term_relationships
ON (wp_posts.ID = wp_term_relationships.object_id)
INNER JOIN wp_postmeta
ON ( wp_posts.ID = wp_postmeta.post_id )
WHERE 1 = 1
AND ( wp_term_relationships.term_taxonomy_id IN (1890041) )
AND ( wp_postmeta.meta_key = 'anyx-training-order' )
AND ((wp_posts.post_type = 'anyx-training'
AND (wp_posts.post_status = 'publish'
OR wp_posts.post_status = 'complete'
OR wp_posts.post_status = 'private')))
GROUP BY wp_posts.ID
ORDER BY min_meta_value ASC;

在这个修改后的查询中,使用 MIN(wp_postmeta.meta_value) 计算每个 wp_posts.ID 对应的最小 meta_value,并将其命名为 min_meta_value,然后按照 min_meta_value 进行排序。

2. 调整 SQL 模式

你也可以选择暂时或者永久地禁用 ONLY_FULL_GROUP_BY 模式,但这种方法并不推荐,因为 ONLY_FULL_GROUP_BY 模式有助于保证查询结果的准确性和一致性。

  • 临时禁用:在当前会话中禁用 ONLY_FULL_GROUP_BY 模式:

收起

sql

SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
  • 永久禁用:修改 MySQL 配置文件(通常是 my.cnf 或者 my.ini),在 [mysqld] 部分添加或者修改 sql_mode 参数:

收起

plaintext

[mysqld]
sql_mode = "STRICT_TRANS_TABLES,ERROR_FOR_DIVISION_BY_ZERO,NO_ZERO_DATE,NO_ZERO_IN_DATE,ERROR_FOR_DIVISION_BY_ZERO"

修改完成后,重启 MySQL 服务使配置生效。

这是写法问题错误吧?order by排序列,必须group by展示出来的吧,否则怎么排序???SQL优化器都没法识别和工作吧。

:yum:修改sql_mode可以识别和工作,但是不建议这么做。

此话题已在最后回复的 7 天后被自动关闭。不再允许新回复。