MySQL:如果任何一行匹配,则选择分组

3
假设我有这样一张表格:
+----+----------+
|name|testValue |
+----+----------+
|A   |3         |
+----+----------+
|A   |4         |
+----+----------+
|A   |7         |
+----+----------+
|B   |0         |
+----+----------+
|B   |3         |
+----+----------+
|C   |5         |
+----+----------+
|C   |5         |
+----+----------+
|C   |6         |
+----+----------+

现在我想按名称对列进行分组,并只能检索满足某些条件的行所在的组。对于每个组,我想检索所有的“testValues”,目前我是通过使用GROUP_CONCAT来实现这一点。

例如,如果我想检索所有“testValues”之间为4到8的任何分组:

SELECT
    name,
    testValue,
    GROUP_CONCAT(testValue SEPARATOR '#') AS testValues
FROM myTable 
GROUP BY name 
HAVING testValue > 4 AND testValue < 8

但这只检查了组中第一行的“testValue”是否匹配。在这个例子中,我的预期输出应该是:
+----+---------+----------+
|name|testValue|testValues|
+----+---------+----------+
|A   |3        |3#4#7     |
+----+---------+----------+
|C   |5        |5#5#6     |
+----+---------+----------+

我的示例查询的实际输出是:

+----+---------+----------+
|name|testValue|testValues|
+----+---------+----------+
|C   |5        |5#5#6     |
+----+---------+----------+

我的问题: 1. 如何使它检查是否有任何一行匹配,而不仅仅是第一行?我是否应该使用HAVING来实现这一点?

  1. 此外,有没有更好的方法从一组行中返回所有值,而不是使用GROUP_CONCAT?

顺便说一句,我尝试过在谷歌上搜索,但发现这很困难。


定义“first”。您没有主键,这可能会带来问题。 - Strawberry
我已经检查过了,结果就是你想要的:http://sqlfiddle.com/#!9/816da/5 - Kostas Mitsarakis
2个回答

7

您可以使用:

SELECT name, MIN(testValue) AS testValue, 
       GROUP_CONCAT(testValue SEPARATOR '#') AS testValues
FROM mytable
GROUP BY name
HAVING COUNT(CASE WHEN testValue BETWEEN 4 AND 8 THEN 1 END) > 0 

点击此处查看演示

HAVING 子句使用 条件聚合 来计算在 [4-8] 范围内的 testValue 值的数量。这将分别应用于每个 name 组。

只有满足 HAVING 断言的组才会被查询返回。因此,只有拥有至少一个行的组,并且该行的 testValue 在 [4-8] 范围内的组才会被返回。

注意:不清楚您希望哪个值作为返回的 testValue。在 OP 提供的样本结果集中选择了最小值。如果您想获取落在 [4-8] 范围内的实际值,则可以使用:

GROUP_CONCAT(CASE 
               WHEN testValue BETWEEN 4 AND 8 
               THEN testValue 
             END SEPARATOR '#') AS testValue

在你的查询语句的 SELECT 子句中。


我只选择了testValue列,否则我会收到错误消息:Error Code: 1054. Unknown column 'testValue' in 'having clause'。这实际上并不是我需要从查询中返回的内容。 - Istlemin
@Istlemin 好的,我明白了,这是由于在查询的 HAVING 子句中使用了非聚合列。这些列通常在 HAVING 中与聚合函数一起使用,比如我答案中使用的 COUNT 函数。 - Giorgos Betsos
不建议在对性能敏感的情况下使用。 - Nick Allen

2

您正在使用部分分组,因此出现了“仅匹配组中第一行”的问题。

您可以使用内部查询来查找所有符合条件的名称。然后在外部查询中查找这些名称。使用适当的索引,此方法可能优于INHAVING解决方案。

SELECT mytable.name, GROUP_CONCAT(testValue SEPARATOR '#') AS testValues
FROM mytable
INNER JOIN (
  SELECT DISTINCT name
  FROM mytable
  WHERE testValue BETWEEN 4 AND 8
) AS subquery ON mytable.name = subquery.name
GROUP BY mytable.name

SQL Fiddle


这个或者Giorgos的解决方案哪个更快? - Istlemin
这取决于您定义的数据和索引。我建议您分析查询,这将告诉您每个查询所花费的时间。 - Salman A

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接