MySQL GROUP_CONCAT 'Invalid use of Group function' 不涉及聚合函数的错误

MySQL GROUP_CONCAT 'Invalid use of Group function' error that DOESN'T involve an aggregate function

我有一个 MySQL 存储过程(如下所示),它应该从 table 层级相关记录构建 ID 列表。我不得不重新设计一个较旧的存储过程,以从使用简单的 CONCAT 函数切换到 GROUP_CONCAT,因为前者无法处理正在生成的列表的大小(即,列表在CONCAT 函数的 1024 个字符限制)。

DELIMITER $$
CREATE PROCEDURE SPTest (top_id INT)
BEGIN
    DECLARE ids_all TEXT;
    SET SESSION group_concat_max_len = 1000000;
    SET ids_all = top_id;
    SET @str = GROUP_CONCAT('SELECT GROUP_CONCAT(id SEPARATOR \', \') ',
        'FROM tbl WHERE nha_id = ', top_id, ' INTO @ids_tmp' SEPARATOR '');
    PREPARE stmt FROM @str;
    EXECUTE stmt;
    WHILE @ids_tmp != "" DO
        SET ids_all = GROUP_CONCAT(ids_all, @ids_tmp SEPARATOR ', ');
        SET @str = GROUP_CONCAT('SELECT GROUP_CONCAT(id SEPARATOR \', \') ',
            'FROM tbl WHERE nha_id IN (', @ids_tmp, ') INTO @ids_tmp' SEPARATOR '');
        PREPARE stmt FROM @str;
        EXECUTE stmt;
    END WHILE;
    SELECT ids_all AS ids;
END $$
DELIMITER ;

问题是当我尝试调用此例程时生成以下错误(有时 returns 当我尝试创建存储过程时出现此错误):

ERROR 1111 (HY000): Invalid use of group function

当我手动创建此代码将构建的相同类型的查询并在命令行 运行 它们时,它们工作得非常好——没有任何类型的错误,我得到了我期望的结果.我看过帖子(在 Stack Exchange 和其他地方)都说 MySQL 不支持嵌套聚合函数,但这里的连接只是对字符串进行的。所以我想也许不知何故它在字符串中看到 GROUP_CONCAT 并因此而打嗝,所以我尝试用“XXXX”代替字符串中的“GROUP_CONCAT”然后使用 REPLACE 函数将其切换回来,但这没有任何区别。我还尝试将 WHEREHAVING 作为标准子句,但都没有用。我还进行了广泛的网络搜索,但未能找到任何有帮助的内容。

我不知道还能尝试什么,主要是因为我看不出这里的语法有什么问题。任何帮助将不胜感激。

更新 1:

此后我尝试了脚本的修改版本,其中 GROUP_CONCAT 合并来自子查询的数据,如下所示:

SET @qrystr = GROUP_CONCAT('SELECT GROUP_CONCAT(c SEPARATOR ", ") ',
    'FROM (SELECT id AS c FROM tbl WHERE nha_id IN (', @ids_tmp, 
    ') AS d INTO @ids_tmp' SEPARATOR '');

但这也没什么区别。

您不能将 GROUP_CONCAT() 用作标量函数;它必须在一组行的上下文中使用。同样,如果没有 table 引用,则不能使用任何其他聚合函数:

SET @x = MAX(<expr>); -- makes no sense

理想情况下,您应该升级到 MySQL 8.0(如果您还没有),并改用 recursive CTE query

WITH RECURSIVE hierarchy AS (
  SELECT top_id AS id
  UNION
  SELECT tbl.id FROM tbl JOIN hierarchy ON tbl.nha_id = hierarchy.id
)
SELECT GROUP_CONCAT(id SEPARATOR ', ') AS ids_all FROM hierarchy;

这将消除使用循环或 prepare/execute 或临时变量的需要。

感谢 Bill Karwin 关于需要将 SELECTGROUP_CONCAT 一起使用的回答,我能够看到我的代码需要如何更改。我不需要使用 SET 语句来为变量赋值,而是需要使用 SELECT ... INTO ... 结构,如下所示:

DELIMITER $$
CREATE PROCEDURE ASSEMBLY_LIST_TEST (top_id INT)
BEGIN
    DECLARE ids_all TEXT DEFAULT '';
    SET SESSION group_concat_max_len = 1000000;
    SET ids_all = top_id;

    # Find the 1st-level children of 'top_id' to start building the list
    SELECT GROUP_CONCAT('SELECT GROUP_CONCAT(id SEPARATOR ", ") FROM tbl ',
        'WHERE nha_id = ', top_id, ' INTO @ids_tmp' SEPARATOR '') INTO @qrystr;
    PREPARE stmt FROM @qrystr;
    EXECUTE stmt;

    # Recursively find the children of each level of children of the previous loop & add to the list
    WHILE @ids_tmp != '' DO
        SELECT GROUP_CONCAT(ids_all, ', ', @ids_tmp SEPARATOR '') INTO ids_all;
        SELECT GROUP_CONCAT('SELECT GROUP_CONCAT(id SEPARATOR ", ") FROM tbl ',
            'WHERE nha_id IN (', @ids_tmp, ') INTO @ids_tmp' SEPARATOR '') INTO @qrystr;
        PREPARE stmt FROM @qrystr;
        EXECUTE stmt;
    END WHILE;
    SELECT ids_all AS ids;
END $$
DELIMITER ;

这现在产生了我正在寻找的结果。当然,递归查询方法是我想要做的更好的解决方案,但也许我的解决方案会对其他人有所帮助。