当 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

我想执行一个查询,比如获取数据库中任何类型的最后一条记录,在我的本地主机上,我使用 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 选项上添加这个,或者将查询更改为具有相同结果的其他查询,并且不会损失性能

有几种方法可以“绕过”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() 函数时,您必须将 table 中的所有列写入 SELECT 并附加 ANY_VALUE() 除了您在GROUP BY.

使用 MAX() 或 MIN() 可以 return 结果,但仍然可能不正确 结果,尤其是对于计数超过 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 可以查看未分组列中的值。将结果与上面的其他查询进行比较,您可以看到 return 多于一个计数的行,其他查询 return 是否符合您的要求?

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:GROUPMySQL & MariaDB中都是保留字。您可以将它用作列名,但您必须始终添加反引号来定义列,否则,运行查询将 return 您出现类似

的错误

Query: select * from table_a group by group

Error Code: 1064 You have an error in your SQL syntax; check the manual that corresponds to your MariaDB server version for the right syntax to use near 'group' at line 1

ONLY_FULL_GROUP_BY 一件好事 ,它执行基本的 ANSI SQL 规则。不要更改它,而是修复您的代码。

从那里开始:你想要完整的记录,所以你不应该考虑聚合,而是过滤

那么:在数据库table中,记录是无序;为了使您的问题有意义,您需要一个列来定义每组中行的顺序,以便明确“最后”记录的含义。让我假设您有这样的专栏,名为 id.

这是解决每组前 1 问题的典型方法,使用相关子查询进行过滤:

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,您可以使用 window 功能:

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