将GROUP_CONCAT用作条件

后端开发 2026-07-09

我正在创建一个查询,其中有一个箱子,它包含多种产品。我想检查任意一个产品的product_numbers是否能在addons表中找到。因此我最初写了这个查询:

SELECT 
    b.name,
    GROUP_CONCAT(DISTINCT p.number) product_numbers,
    (
        SELECT COUNT(id) FROM addons WHERE addons.product_code IN (product_numbers)
    ) `addon_count`
FROM box b
LEFT JOIN product p ON p.box_ident = b.ident
GROUP BY b.ident;

这段查询不起作用。我不确定接下来该怎么继续。如何根据product_numbers的值返回箱子中addons的数量?

解决方案

下面是一个解决方案:

SELECT 
    b.name,
    GROUP_CONCAT(DISTINCT p.number) product_numbers,
    COUNT(DISTINCT a.product_code) AS addon_count
FROM box b
LEFT JOIN product p ON p.box_ident = b.ident
LEFT JOIN addons a ON p.number = a.product_code
GROUP BY b.ident;

这个额外的连接确实会增加预聚合结果的行数,但两个聚合函数都使用 DISTINCT,因此在本例中可以抵消这一点。将来如果在聚合时不使用 DISTINCT 的查询中使用它,请记在心。

The addon_count 实际上并不真正统计addons,它统计的是在给定箱子中有至少一个addon的产品数量。看起来这就是如果你的查询能工作时它应该实现的效果。

演示:https://dbfiddle.uk/9-T9ICkb

你的查询在以下几个方面无法工作:

  • GROUP_CONCAT() 返回的是一个用逗号分隔的字符串,而不是可用于 IN() 谓词的列表。
  • 在SELECT列表中的表达式不能引用同一SELECT列表中由另一表达式创建的别名(编辑:这对聚合表达式也成立,见下方Akina的评论)。
  • 表达式的求值先后顺序未定义,因此你也不能指望在子查询被求值之前就先填充 product_numbers

下面的示例展示了Barmar的注释所说的内容:

SELECT
  t.name,
  t.product_numbers,
  COUNT(DISTINCT a.product_code) AS addon_count
FROM (
  SELECT
      b.ident,
      b.name,
      GROUP_CONCAT(DISTINCT p.number) product_numbers
  FROM box b
  LEFT JOIN product p ON p.box_ident = b.ident
  GROUP BY b.ident
) t
LEFT JOIN addons a ON FIND_IN_SET(a.product_code, t.product_numbers)
GROUP BY t.ident;

这个查询仍然不能在同一个SELECT列表中引用聚合函数的结果,因此我们必须把前面的查询放到一个子查询中。

我不推荐这条查询。它有一个重要的缺点:FIND_IN_SET() 从不使用索引,因此对 addons 的连接很可能优化不佳。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章