如何解决GROUP_CONCAT具有多个列的分组依据
我在选择GROUP_CONCAT时遇到了问题,该行也应该包含与该问题类似的行号GROUP_CONCAT numbering 区别在于我必须按多列分组。
例如,我有2个表review
和review_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_id
和category_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调用中编号的方法吗?
解决方法
您应避免在生产代码中使用用户定义的变量。
作为一般规则,除了在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 |
,
您需要使用两步子查询来按审阅者编号排序。
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
;
,
为完整起见,我还添加了解决方案,该解决方案如何在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
;
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。