MariaDB:带有子查询的 group_concat 失败

Posted

技术标签:

【中文标题】MariaDB:带有子查询的 group_concat 失败【英文标题】:MariaDB: group_concat with subquery failing 【发布时间】:2018-02-05 22:19:38 【问题描述】:

mysql 5.6.34(我的新开发服务器)和 MariaDB 10.2.8(我的新生产服务器,我以为我今天终于部署代码的地方——叹息!)上的数据库结构和数据完全相同,MySQL 是工作而 MariaDB 没有。这是多年来在 MySQL 5.0.95 上运行良好的代码。我已将查询简化为显示问题的最小示例 - 似乎 GROUP_CONCAT() 和子查询不混合。这是查询:

SELECT person.PersonID,
GROUP_CONCAT(CategoryID ORDER BY CategoryID SEPARATOR ',') AS categories
FROM person LEFT JOIN percat ON person.PersonID=percat.PersonID
WHERE person.PersonID IN (SELECT PersonID FROM action WHERE ActionTypeID=3)
GROUP BY person.PersonID

这是一张合成的屏幕截图,显示了所有三个表的结构:

在 MySQL 上,它运行良好,因为它已经运行了多年。这是结果和EXPLAIN

这是我在 MariaDB 上得到的疯狂结果:

我不太了解数据库引擎的内部工作原理,无法关注EXPLAIN,但我认为线索就在某处。我发现this bug report 听起来很相关,但我真的不明白他们在说什么,更重要的是,我应该怎么做。

【问题讨论】:

【参考方案1】:

这是一个错误,显然它与您发现的不完全相同(因为上述错误报告中的测试用例在 10.2.8 上运行良好,而您的确实不行)。请随时通过MariaDB JIRA 报告新的。

同时,我认为您应该能够通过设置来解决它

optimizer_switch=orderby_uses_equalities=off

在您的 cnf 文件中。这是一个新启用的优化,显然不是完美无缺的。


更新:该错误现在报告为https://jira.mariadb.org/browse/MDEV-13694

【讨论】:

如果关闭,我似乎无法关闭 - 显然我做错了。在/etc/my.cnf[mysqld] 部分,我添加了optimizer_switch = orderby_uses_equalities=off(我尝试了带单引号和不带单引号 - 我在两者的网络上看到了示例)并重新启动了 mysqld(它也有奇怪的行为 - 它没有给出我的ssh 提示返回,直到我在等待一段时间后按 ^C)。但是SELECT @@optimizer_switch\Gorderby_uses_equalities 还在,查询行为没有变化。 截面和线条都很好。首先,检查您的服务器是否确实重新启动(例如通过验证SHOW STATUS LIKE 'Uptime' 和/或错误日志。)听起来服务器并没有真正关闭,然后尝试重新启动,并开始争夺数据目录,例如用于锁定 aria 控制文件等。如果服务器确实重新启动,则另一个可能的原因是您的安装未使用此 cnf 文件。您可以在 optimizer_switch 之后放置一些独特的东西,例如lock_wait_timeout=42(或任何你能认出的),看看它是否被捡起。 是的,就是这样 - 它实际上并没有重新启动(我的命令错误)。您的解决方法就像一个魅力!当我有更多时间时,我会简化我的代码,但是现在,我可以继续启动我的新服务器,感谢你。 感谢您报告错误。我必须先注册为 JIRA 的成员,但我还没有开始。 @OsakaWebbie - 向我们展示整个 SET 声明。也许你在使用 GLOBAL 而应该使用 SESSION【参考方案2】:

解决方法这无法回答为什么会有差异,但您应该将DISTINCT 添加到GROUP_CONCAT

“为什么” 答案可能深深植根于优化器中。自 5.0 以来发生了很多变化。 5.6有很多新代码;与此同时,MariaDB 正在分叉到 10.0。在这次分叉中,优化器出现了显着差异。 10.2 更进一步,但未必在优化这种类型的查询

改进的查询 可以对查询执行几项操作。有些可能会更快:

SELECT  p.PersonID, 
        ( SELECT  GROUP_CONCAT(pc.CategoryID
                               ORDER BY  CategoryID SEPARATOR ',')
            FROM  percat
            WHERE  PersonID = p.PersonID 
        ) AS categories
    FROM  person
    JOIN  action AS a  ON p.PersonID = a.PersonID
    WHERE  ActionTypeID = 3
    GROUP BY  p.PersonID 

转换LEFT JOIN 可能会减少GROUP BY 的负载。可能GROUP BY 可以删除。

因为PRIMARY KEY(PersonID, CategoryID),应该不需要DISTINCT

需要的索引这个“覆盖索引”会加快速度:INDEX(ActionTypeID, PersonID)

【讨论】:

添加 DISTINCT 只消除了重复的 CategoryID - 它对主要问题没有影响:当应该有多个时,我仍然只得到一个人记录。至于您通过将子查询更改为连接来优化的想法,请注意我在这里共享的查询非常简化 - 在我的实际应用程序中,用户可以组合各种搜索条件,我必须以编程方式进行任何他们选择一个工作查询。如果没有子查询,我无法找到一种方法。 GROUP BY PersonID 导致每人只有一行。我想我不知道你想要什么。而且您的样本输出每人只有一行。模拟你想要的输出。 有四个人匹配 WHERE,所以应该返回四行。正确的输出可以在“On MySQL...”屏幕截图中看到。我不知道为什么前三个人没有返回,但这是主要问题。

以上是关于MariaDB:带有子查询的 group_concat 失败的主要内容,如果未能解决你的问题,请参考以下文章

如何在 from 子句中使用子查询创建视图 - Mariadb

如何在带有 MIN 的 SELECT 中包含第三列

MySQL/MariaDB - 按内部子查询排序

EXISTS 子查询导致错误 1064

mariadb的explain分析及InnoDB存储引擎

不使用带有 order 子句和更大限制 MariaDB 的索引的简单 SQL 查询