GROUP_CONCAT具有多个列的分组依据

问题描述

我在选择GROUP_CONCAT时遇到了问题,该行也应该包含与该问题类似的行号GROUP_CONCAT numbering 区别在于我必须按多列分组。

例如,我有2个表reviewreview_detail
模式(MysqL v5.5)

create table review (
  `id` int(11) NOT NULL AUTO_INCREMENT,`submission_id` int(11) NOT NULL,PRIMARY KEY (`id`)
);

create table review_detail (
  `id` int(11) NOT NULL AUTO_INCREMENT,`review_id` int(11),`category_id` int(11),`rating` varchar(100),PRIMARY KEY (`id`)
);

insert into review (`id`,`submission_id`) values (1,1),(2,(3,2),(4,3),(5,(6,(7,(8,3);

insert into review_detail (`review_id`,`category_id`,`rating`)
values 
(1,1,' submission 1.1 cat 1'),(1,2,' submission 1.1 cat 2'),' submission 1.2 cat 1'),' submission 1.2 cat 2'),' submission 2.1 cat 1'),' submission 2.1 cat 2'),' submission 3.1 cat 1'),' submission 1.3 cat 1'),' submission 1.3 cat 2'),' submission 3.2 cat 1'),' submission 3.2 cat 2'),' submission 2.2 cat 1'),' submission 2.2 cat 2'),' submission 3.3 cat 1'),' submission 3.3 cat 2')
;

查询#1

SELECT * FROM review;

| id  | submission_id |
| --- | ------------- |
| 1   | 1             |
| 2   | 1             |
| 3   | 2             |
| 4   | 3             |
| 5   | 1             |
| 6   | 3             |
| 7   | 2             |
| 8   | 3             |

查询#2

SELECT * FROM review_detail;

| id  | review_id | category_id | rating                |
| --- | --------- | ----------- | --------------------- |
| 1   | 1         | 1           |  submission 1.1 cat 1 |
| 2   | 1         | 2           |  submission 1.1 cat 2 |
| 3   | 2         | 1           |  submission 1.2 cat 1 |
| 4   | 2         | 2           |  submission 1.2 cat 2 |
| 5   | 3         | 1           |  submission 2.1 cat 1 |
| 6   | 3         | 2           |  submission 2.1 cat 2 |
| 7   | 4         | 1           |  submission 3.1 cat 1 |
| 8   | 4         | 2           |  submission 3.1 cat 1 |
| 9   | 5         | 1           |  submission 1.3 cat 1 |
| 10  | 5         | 2           |  submission 1.3 cat 2 |
| 11  | 6         | 1           |  submission 3.2 cat 1 |
| 12  | 6         | 2           |  submission 3.2 cat 2 |
| 13  | 7         | 1           |  submission 2.2 cat 1 |
| 14  | 7         | 2           |  submission 2.2 cat 2 |
| 15  | 8         | 1           |  submission 3.3 cat 1 |
| 16  | 6         | 2           |  submission 3.3 cat 2 |

每个提交的评论(外键= submission_id)都有多个带有category_id的review_detail条目(在我的示例中,只有2个类别(1,2)与查询无关)。 / p>

我必须创建一个选择,使我得到按submission_idcategory_id分组的GROUP_CONCAT。

Concat字符串应返回
Reviewer 1: {rating},Reviewer 2: {rating},Reviewer 3: {rating} etc.

例如对于submitt_id = 1和category_id = 1,组concat应该返回
Reviewer 1: submission 1.1 cat 1,Reviewer 2: submission 1.2 cat 1,Reviewer 3: submission 1.3 cat 1

但是我无法正确设置concat组中的编号。

到目前为止,我已经进行了多次测试。

只有一个列计数器的组(有效)
https://www.db-fiddle.com/f/6hA4Vft1mQGdw2Pew2An2T/3
Reviewer 1: submission 1.1 cat 1 of review 1 / Reviewer 2: submission 3.3 cat 1 of review 8 / Reviewer 3: submission 2.2 cat 1 of review 7 / Reviewer 4: submission 3.2 cat 1 of review 6 / ... etc.

SELECT
    --review.submission_id,review_detail.category_id,@i,GROUP_CONCAT(
        CONCAT(
            'Reviewer ',@i := @i + 1,': ',rating,' of review ',review_id
        )
    SEParaTOR ' / '
    ) concatText,@i := 0
FROM
    review_detail
LEFT JOIN review ON review.id = review_detail.review_id,(
SELECT
    @i := 0
) init
GROUP BY
    review_detail.category_id
ORDER BY
    review_detail.category_id ASC
;

使用if和比较2个分组列的字符串进行测试(不起作用)
https://www.db-fiddle.com/f/3woAVSw5hrav15jAmuWVdT/3
Reviewer 1: submission 1.1 cat 1 of review 1 / Reviewer 1: submission 1.2 cat 1 of review 2 / Reviewer 1: submission 1.3 cat 1 of review 5

SELECT
    submission_id,category_id,@grp,CONCAT_WS("-",submission_id,category_id) AS catgroup,@i := IF(
                @grp = CONCAT_WS("-",category_id),@i + 1,IF(
                    @grp := CONCAT_WS("-",1
                )
            ),review_id
        )
    ORDER BY review_id,category_id 
    SEParaTOR ' / '
    ) concatText
FROM
    review_detail
LEFT JOIN review ON review.id = review_detail.review_id,(
SELECT
    @i := 0,@grp := ''
) init
GROUP BY
    review.submission_id,review_detail.category_id

那么有人对多列进行分组时,有谁知道正确地在GROUP_CONCAT调用中编号的方法吗?

解决方法

您应避免在生产代码中使用用户定义的变量。

manual for MySQL 5.6中说:

作为一般规则,除了在SET语句中,永远不要 为用户变量分配一个值,并在同一变量中读取该值 声明。

甚至在documentation for 8.0中也声明:

涉及用户变量的表达式的求值顺序为 未定义。例如,不能保证SELECT @a,@a:=@a+1 首先评估@a,然后执行分配。

在将来的版本中,这可能完全不再起作用:

以前的MySQL版本可以为一个值分配一个值 SET以外的语句中的用户变量。此功能是 MySQL 8.0支持向后兼容,但受制于 在将来的MySQL版本中删除。

所以这是一个没有用户定义变量的解决方案:

SELECT 
r.submission_id,rd.category_id,GROUP_CONCAT(CONCAT('Reviewer ',(SELECT COUNT(*) + 1 
                                  FROM review 
                                  JOIN review_detail ON review.id = review_detail.review_id 
                                  WHERE r.submission_id = review.submission_id 
                                  AND review_detail.category_id = rd.category_id 
                                  AND review_detail.id < rd.id
                                 ),': ',rating,' of review ',review_id) ORDER BY rating SEPARATOR ' / ') AS shorter_column_name
FROM 
review r 
JOIN review_detail rd ON rd.review_id = r.id
GROUP BY r.submission_id,rd.category_id;

返回

+---------------+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
| submission_id | category_id | shorter_column_name                                                                                                                           |
+---------------+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
|             1 |           1 | Reviewer 1:  submission 1.1 cat 1 of review 1 / Reviewer 2:  submission 1.2 cat 1 of review 2 / Reviewer 3:  submission 1.3 cat 1 of review 5 |
|             1 |           2 | Reviewer 1:  submission 1.1 cat 2 of review 1 / Reviewer 2:  submission 1.2 cat 2 of review 2 / Reviewer 3:  submission 1.3 cat 2 of review 5 |
|             2 |           1 | Reviewer 1:  submission 2.1 cat 1 of review 3 / Reviewer 2:  submission 2.2 cat 1 of review 7                                                 |
|             2 |           2 | Reviewer 1:  submission 2.1 cat 2 of review 3 / Reviewer 2:  submission 2.2 cat 2 of review 7                                                 |
|             3 |           1 | Reviewer 1:  submission 3.1 cat 1 of review 4 / Reviewer 2:  submission 3.2 cat 1 of review 6 / Reviewer 3:  submission 3.3 cat 1 of review 8 |
|             3 |           2 | Reviewer 1:  submission 3.1 cat 1 of review 4 / Reviewer 2:  submission 3.2 cat 2 of review 6 / Reviewer 3:  submission 3.3 cat 2 of review 6 |
+---------------+-------------+-----------------------------------------------------------------------------------------------------------------------------------------------+
,

修复您的查询。

基本问题是表本质上是未排序的,这就是MySQL优化程序删除ORDER BY的原因。

在MySQL中,将所有表放在FROM子句中足以使广告按顺序创建子查询,mysql会保留它。

在Mariadb中这已经足够了,您还添加了LIMIT 18446744073709551615,以便优化程序将其保留

模式(MySQL v5.5)

查询#1

SELECT
    submission_id,category_id,@i,@grp,CONCAT_WS("-",submission_id,category_id) AS catgroup,GROUP_CONCAT(
        CONCAT(
            'Reviewer ',@i := IF(
                @grp = CONCAT_WS("-",category_id),@i := @i + 1,IF(
                    @grp := CONCAT_WS("-",1,1
                )
            ),review_id
        )
    ORDER BY review_id,category_id 
    SEPARATOR ' / '
    ) concatText
FROM
    (SELECT review_id,`rating` FROM review_detail
LEFT JOIN review ON review.id = review_detail.review_id
     ORDER BY review_id,category_id ) t1,(
SELECT
    @i := 0,@grp := ''
) init


GROUP BY
    submission_id,category_id;

结果

| submission_id | category_id | @i  | @grp | catgroup | concatText                                                                                                                                    |
| ------------- | ----------- | --- | ---- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
| 1             | 1           | 0   |      | 1-1      | Reviewer 3:  submission 1.1 cat 1 of review 1 / Reviewer 2:  submission 1.2 cat 1 of review 2 / Reviewer 1:  submission 1.3 cat 1 of review 5 |
| 1             | 2           | 3   | 1-1  | 1-2      | Reviewer 3:  submission 1.1 cat 2 of review 1 / Reviewer 2:  submission 1.2 cat 2 of review 2 / Reviewer 1:  submission 1.3 cat 2 of review 5 |
| 2             | 1           | 3   | 1-2  | 2-1      | Reviewer 1:  submission 2.1 cat 1 of review 3 / Reviewer 2:  submission 2.2 cat 1 of review 7                                                 |
| 2             | 2           | 2   | 2-1  | 2-2      | Reviewer 2:  submission 2.1 cat 2 of review 3 / Reviewer 1:  submission 2.2 cat 2 of review 7                                                 |
| 3             | 1           | 2   | 2-2  | 3-1      | Reviewer 2:  submission 3.1 cat 1 of review 4 / Reviewer 1:  submission 3.2 cat 1 of review 6 / Reviewer 3:  submission 3.3 cat 1 of review 8 |
| 3             | 2           | 3   | 3-1  | 3-2      | Reviewer 3:  submission 3.1 cat 1 of review 4 / Reviewer 2:  submission 3.3 cat 2 of review 6 / Reviewer 1:  submission 3.2 cat 2 of review 6 |

View on DB Fiddle

,

您需要使用两步子查询来按审阅者编号排序。

SET @i := 0;
SET @grp := '';
SELECT
    submission_id,GROUP_CONCAT(
      CONCAT(
        'Reviewer ',i,review_id
      )
      ORDER BY i
      SEPARATOR ' / '
    ) concatText
FROM
-- second,add numbering
(
  SELECT *,@i := IF(
      @grp = @grp := CONCAT_WS('-',@i + 1,1) i
  FROM
  -- first,sort for numbering
  (
    SELECT
        review_id,rating
    FROM review_detail LEFT JOIN review ON review.id = review_detail.review_id
    ORDER BY
        submission_id,review_id
  ) t1
) t2
GROUP BY
    submission_id,category_id
;

db fiddle

,

为完整起见,我还添加了解决方案,该解决方案如何在Mysql 8.0中完成

与COUNT(*)一起使用

with base as (
    
  SELECT
    review_id,count(*) over (partition by submission_id,category_id  order by review_id) num
  
    FROM review_detail LEFT JOIN review ON review.id = review_detail.review_id
    ORDER BY
        submission_id,review_id
)
select   
  submission_id,group_concat(concat('Reviewer',num,review_id ) separator ',') concattext
from     base
group by 
submission_id,category_id
;

OR ROW_NUMBER()

with base as (
        SELECT
            review_id,ROW_NUMBER() over (partition by submission_id,category_id  order by review_id) num
        FROM review_detail 
        LEFT JOIN review ON review.id = review_detail.review_id
        ORDER BY
            submission_id,review_id
    )
    SELECT   
        submission_id,review_id  ) separator ',') concattext
    from base
    group by 
        submission_id,category_id
;

DB Fiddle