按多列分组并标记唯一的子组

时间:2014-09-09 01:57:01

标签: mysql sql group-by

从这些数据开始:

+--------+------------+--------+--------+
| Fruit  | Vegetables | Colors | Number |
+--------+------------+--------+--------+
| Apple  | Beans      | Blue   |     10 |
| Apple  | Beans      | Blue   |     10 |
| Apple  | Beans      | Blue   |     20 |
| Apple  | Beans      | Blue   |     20 |
| Apple  | Beans      | Green  |     20 |
| Apple  | Beans      | Green  |     20 |
| Banana | Brocoli    | Red    |     10 |
| Banana | Brocoli    | Red    |     10 |
| Banana | Brocoli    | Blue   |     10 |
| Banana | Beans      | Blue   |     20 |
| Banana | Beans      | Green  |     20 |
| Banana | Beans      | Green  |     20 |
+--------+------------+--------+--------+

我想通过他们的水果来计算蔬菜,颜色和数字的独特组合。所以我从这个查询开始。

SET @a:=0;

select *, @a:=@a+1 as newid FROM(
select fruit, vegetable, color, number

FROM test

GROUP BY fruit, vegetable, color, number) as info

获得此数据

+-------+--------+------------+--------+--------+
| newid | Fruit  | Vegetables | Colors | Number |
+-------+--------+------------+--------+--------+
|     1 | Apple  | Beans      | Blue   |     10 |
|     2 | Apple  | Beans      | Blue   |     20 |
|     3 | Apple  | Beans      | Green  |     20 |
|     4 | Banana | Brocoli    | Red    |     10 |
|     5 | Banana | Brocoli    | Blue   |     10 |
|     6 | Banana | Beans      | Blue   |     20 |
|     7 | Banana | Beans      | Green  |     20 |
+-------+--------+------------+--------+--------+

但我的最终目标是到达

+----------+--------+------------+--------+--------+
|    ID    | Fruit  | Vegetables | Colors | Number |
+----------+--------+------------+--------+--------+
| Apple-1  | Apple  | Beans      | Blue   |     10 |
| Apple-1  | Apple  | Beans      | Blue   |     10 |
| Apple-2  | Apple  | Beans      | Blue   |     20 |
| Apple-2  | Apple  | Beans      | Blue   |     20 |
| Apple-3  | Apple  | Beans      | Green  |     20 |
| Apple-3  | Apple  | Beans      | Green  |     20 |
| Banana-1 | Banana | Brocoli    | Red    |     10 |
| Banana-1 | Banana | Brocoli    | Red    |     10 |
| Banana-2 | Banana | Brocoli    | Blue   |     10 |
| Banana-3 | Banana | Beans      | Blue   |     20 |
| Banana-4 | Banana | Beans      | Green  |     20 |
| Banana-4 | Banana | Beans      | Green  |     20 |
+----------+--------+------------+--------+--------+

我意识到最终我可以使用group_concat,但我不确定在查询中重新开始计数器的效果如何。

2 个答案:

答案 0 :(得分:2)

你有个好的开始。你对GROUP BY的困难是正确的。你可以这样做,但是你需要一个JOIN操作回到原始表,以获得所有行,

这是另一种使用“技巧”的方法:保留用户定义变量中当前行的值,以便可以将这些值与下一行的值进行比较。我们使用条件测试来确定我们是否将计数器保留为现有值,或者将其递增1,或者将其重置为1。

我们在查询中使用ORDER BY子句,以确保按行需要比较的顺序返回行;我们将比较每一行前一行的值。

举个例子:

SELECT CONCAT(r.fruit,'-',r.i) AS ID
     , r.fruit
     , r.vegetable
     , r.color
     , r.number
  FROM ( SELECT IF( t.fruit <=> @fruit
                , IF( t.vegetable <=> @vegetable 
                  AND t.color     <=> @color
                  AND t.number    <=> @number
                  , @i := @i + 0
                  , @i := @i + 1
                  )
                , @i := 1
                ) AS i
              , @fruit     := t.fruit      AS fruit
              , @vegetable := t.vegetable  AS vegetable
              , @color     := t.color      AS color
              , @number    := t.number     AS number
           FROM ( SELECT @i         := 1
                       , @fruit     := NULL
                       , @vegetable := NULL
                       , @color     := NULL
                       , @number    := NULL
                ) i
          CROSS
           JOIN test t
          ORDER 
             BY t.fruit
              , t.vegetable
              , t.color
              , t.number
       ) r

别名为i的内联视图初始化我们的用户定义变量(而不是依赖于单独的语句。)我们并不关心内联视图返回什么,除了它返回1行(因为我们已经指定了一个JOIN操作);我们真正关心的是它在引用它的查询运行之前得到评估。

要测试的CROSS JOIN会从测试中获取所有行。

我们指定一个ORDER BY子句,以便按指定的顺序返回行。这很重要,因为我们将把一行中的值与前一行的值进行比较。

“技巧”是一个表达式,用于比较当前行(egtfruit)中的值与前一行的值:(例如@fruit。)我们使用布尔表达式,如果所有列都匹配则返回TRUE如果这是真的,我们递增计数器(@i)。否则,我们将计数器重置为1.

MySQL不保证评估的顺序,但是我们观察到SELECT列表中的表达式是从左到右评估的,所以我们要小心执行条件测试覆盖保存上一行值的用户定义变量。

我们将整个查询包装在parens中并给它一个别名,这样我们就可以运行另一个外部查询来将生成的计数器连接成一个字符串(按照指定的结果集。)

一些**重要说明((:

此语法特定于MySQL;我们观察到的行为无法保证,但它是我们最接近模仿其他数据库中可用的“分析函数”。

请注意,此行为可能会在MySQL的未来版本中发生变化。

答案 1 :(得分:1)

首先,这会在不同的水果上重置你的计数。

SELECT *, 
       if(@a = fruit, @b := @b + 1, @b := 1) as counter,
       @a := fruit
FROM (
    select fruit, vegetable, color, number
    FROM test
    GROUP BY fruit, vegetable, color, number
) t
CROSS JOIN (SELECT @a := null, @b := null)temp

但是要为您展示的预期结果提供结果。试试这个

SELECT 
    CONCAT(fruit, ' ', counter) as fruit, vegetable, color, number
FROM
(   SELECT 
        *, 
        if(@a = fruit, if(@c = color AND @d = number, @b, @b := @b + 1), @b := 1) as counter,
        @a := fruit, @c := color, @d := number
    FROM test
    CROSS JOIN (SELECT @a := null, @b := null, @c := null, @d := null)temp
)t

基本上对于第二个查询,你要逐行进行比较,以查看前一行中的值是否是下一行中的值,如果它们不是,则重置计数器,如果它们不重置但是递增,并且如果颜色和数字都与前一行匹配,只是放入该计数器值并且不增加它

DEMO

他们必须在这里进行的关键评估是在对计数器进行条件检查之后将赋值放到列变量中。这样你将以前的行值与当前行值进行比较......需要注意的一件事是MySQL并不保证这种评估顺序会以这种方式发生..但通常从左到右进行评估。