当 sql_mode=only_full_group_by 时,在 mysql 中完成 Group By 工作的最佳方法是啥
Posted
技术标签:
【中文标题】当 sql_mode=only_full_group_by 时,在 mysql 中完成 Group By 工作的最佳方法是啥【英文标题】:What is the best way to do the job of Group By in mysql when sql_mode=only_full_group_by当 sql_mode=only_full_group_by 时,在 mysql 中完成 Group By 工作的最佳方法是什么 【发布时间】:2021-03-21 13:29:24 【问题描述】:我想执行一个查询,例如获取数据库中任何类型的最后一条记录,在我的本地主机上,我使用 Maria Db,查询如下:
SELECT *
FROM table_a
WHERE column_a=999
OR column_b=999
GROUP
BY `group`;
group
是我在其中保存类型的列,例如:营销、博客、订单等
此查询在本地运行良好,但在服务器上出现以下错误:
SQLSTATE[42000]:
Syntax error or access violation:
1055 Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column
'db_name.table_a.id' which is not functionally dependent on columns in GROUP BY clause;
this is incompatible with sql_mode=only_full_group_by\n
The SQL being executed was:
SELECT * FROM `table_a` WHERE (`column_a`=999) OR (`column_b`=999) GROUP BY `group`"
根据mysql document,我可以使用以下命令来实现这一点:
SET GLOBAL sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''));
但是我对 Db 没有足够的权限并得到以下错误:
#1227 - Access denied; you need (at least one of) the SUPER privilege(s) for this operation
我让主机帮我做这件事,他们回答说他们不想做这个操作
我使用的是 YII2 框架,现在我想在框架的 database_config.php 选项中添加它,或者将查询更改为具有相同结果的其他内容,并且不损失性能
【问题讨论】:
SELECT *
没有意义。当您执行GROUP BY group
时,具有相同group
值的所有行都将被视为一个组。对于此行组,必须为每一列返回一个值,但存在很多值。如何确定您需要什么值?首先(按什么顺序?)?最大?随机的?还有什么?
托管公司已为sql_mode
配置了正确的值。您不应尝试更改 sql_mode
以允许无效查询。您应该改正您的查询。我写了一个答案来解释为什么这种类型的查询在这里无效:***.com/a/13999903/20860
但我对 Db 没有足够的权限 当然。但是如果你真的想改变 SQL 模式并执行你不合逻辑的查询,那么你必须改变 SESSION 而不是 GLOBAL 设置。
最简单的选择是完全拒绝问题的前提。这简直是荒谬的。
@Akina 这正是我想要的,我想要每种类型的最后一条记录,可能是 8 种类型所以 8 条记录
【参考方案1】:
ONLY_FULL_GROUP_BY
是一件好事,它强制执行基本的 ANSI SQL 规则。不要更改它,而是修复您的代码。
从那里开始:你想要完整的记录,所以你不应该考虑聚合,而应该考虑过滤。
那么:在一个数据库表中,记录是无序的;为了使您的问题有意义,您需要一个定义每个组中行的顺序的列,因此“最后”记录的含义是明确的。假设您有这样的专栏,名为id
。
这是解决此 top-1-per-group 问题的典型方法,使用相关子查询进行过滤:
SELECT *
FROM table_a a
WHERE
999 IN (column_a, column_b)
AND id = (
SELECT MAX(a1.id)
FROM table_a a1
WHERE 999 IN (a1.column_a, a1.column_b) AND a1.grp = a.grp
)
或者,如果您运行的是 MySQL 8.0,则可以使用窗口函数:
SELECT *
FROM (
SELECT a.*,
ROW_NUMBER() OVER(PARTITION BY grp ORDER BY id DESC) rn
FROM table_a a
WHERE 999 IN (column_a, column_b)
) a
WHERE rn = 1
旁注:group
是语言关键字,因此不适合作为列名。我在查询中将其重命名为grp
。
【讨论】:
这是个好办法;但是,我宁愿根据yiiframework.com/doc/guide/2.0/en/db-active-record 使用 Active Record,而不是为我的代码的所有部分编写查询(可能需要重构 100 多个查询)。而且我的代码使用其他托管服务没有这个问题,但是我现在的客户坚持使用这个托管,所以如果我没有找到任何其他解决方案,我会尝试这种方法。【参考方案2】:有几种方法可以“绕过”sql_mode
但请注意,您得到的结果可能不正确。
首先你可以使用ANY_VALUE()。像这样的例子:
SELECT any_value(column_a), any_value(column_b), `group` FROM table_a
WHERE (column_a=999) OR (column_b=999) GROUP BY `group`;
使用ANY_VALUE()
函数时,您必须从表中写入SELECT
中的所有列,并附加ANY_VALUE()
,除了您在GROUP BY
中使用的列。
使用 MAX() 或 MIN() 可以返回结果,但仍然可能不正确 结果,尤其是对于计数超过 1 的任何行:
SELECT MAX(column_a), MAX(column_b), `group`
FROM table_a
WHERE (column_a=999) OR (column_b=999) GROUP BY `group`;
使用GROUP_CONCAT
可以让您了解非分组列中的值。将结果与上面的其他查询进行比较,您可以看到在返回多个计数的行上,其他查询是否根据您的要求返回?
SELECT group_concat(column_a), group_concat(column_b), group_concat(`group`)
FROM table_a
WHERE (column_a=999) OR (column_b=999) GROUP BY `group`;
我不确定您是否可以这样做,但您可以暂时关闭 sql_mode
,然后您应该能够运行您的查询:
SET sql_mode=""; -- you don't need to set global privilege.
SELECT * FROM table_a
WHERE (column_a=999) OR (column_b=999) GROUP BY `group`;
Demo here.
不过,最好的选择是保留 sql_mode
原样并根据要求构造查询。
P/S:GROUP
是 MySQL 和 MariaDB 中的保留字。您可以将其用作列名,但您必须始终添加反引号来定义列,否则,运行查询将返回类似的错误
查询:select * from table_a group by group
错误代码:1064 您的 SQL 语法有错误;查看与您的 MariaDB 服务器版本相对应的手册,了解在第 1 行的“组”附近使用的正确语法
【讨论】:
嗨,我确实使用了 any_value 并将所有列添加到 select 中,我的 4 列有关系,但我仍然得到同样的错误。 GROUP BYgroup
也有同样的错误。对于您的第三个选项,我正在寻找它,我可以逐个查询进行查询,但可能有超过 100 个查询使用了 group by,我正在寻找一种方法来通过设置 Yii2 框架来添加它,到目前为止,没有找到解决方案。以上是关于当 sql_mode=only_full_group_by 时,在 mysql 中完成 Group By 工作的最佳方法是啥的主要内容,如果未能解决你的问题,请参考以下文章
[SUCTF 2019]EasySQL1 及sql_mode
GROUP BY不适用于MySQL 5.7,因为5.7使用SQL_MODE的“ONLY_FULL_GROUP_BY”选项。