mysql GROUP_CONCAT+ GROUP BY + substring_index获取分组的前几名
Posted Little_Rookie
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了mysql GROUP_CONCAT+ GROUP BY + substring_index获取分组的前几名相关的知识,希望对你有一定的参考价值。
mysql方法来源于:http://www.cnblogs.com/jjcc/p/5896588.html
###在网上看到一篇,非常赞的方法###
比如说要获取班级的前3名,mysql就可以用GROUP_CONCAT + GROUP BY + substring_index实现。
考试表
DROP TABLE IF EXISTS `test`;
CREATE TABLE `test` (
`id` int(11) DEFAULT NULL,
`name` varchar(20) DEFAULT NULL,
`score` int(11) DEFAULT NULL,
`class` char(12) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
插入数据
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'1\', \'Bobdd\', \'25\', \'1\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'2\', \'xx\', \'20\', \'2\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'3\', \'Jack\', \'30\', \'2\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'4\', \'Bill\', \'32\', \'4\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'5\', \'Nick\', \'22\', \'3\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'6\', \'Kathy\', \'18\', \'3\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'7\', \'Steve\', \'36\', \'3\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'8\', \'Anne\', \'25\', \'2\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'9\', \'Kathy\', \'18\', \'2\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'11\', \'Bob1\', \'25\', \'3\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'12\', \'Jane1\', \'20\', \'1\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'13\', \'Jack1\', \'30\', \'1\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'14\', \'Bill1\', \'32\', \'1\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'15\', \'Nick1\', \'22\', \'4\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'16\', \'Kathy1\', \'18\', \'4\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'17\', \'Steve1\', \'36\', \'4\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'18\', \'Anne1\', \'25\', \'1\');
INSERT INTO `test` (`id`, `name`, `score`, `class`) VALUES (\'19\', \'Kathy1\', \'18\', \'2\');
运用group_concat + GROUP BY 分组 获取前3名
select GROUP_CONCAT(t1.id) as ids from (
SELECT t.class, substring_index(GROUP_CONCAT(t.id ORDER BY t.score desc),\',\',3) as id from
test t GROUP BY t.class
)t1
得到
注意 是t.id ORDER BY t.score desc 分数从高到低。
上面的语句只是获取到总的id。但是转换为列不太好弄。可以拆分用union all 来搞。
获取第一名
SELECT t.class, substring_index(GROUP_CONCAT(t.id ORDER BY t.score desc),\',\',1) as id from
test t GROUP BY t.class
union all
-- 第二名
SELECT t.class, substring_index(substring_index(GROUP_CONCAT(t.id ORDER BY t.score desc),\',\',2),\',\',-1) as id from
test t GROUP BY t.class
union all
-- 第三名
SELECT t.class, substring_index(substring_index(GROUP_CONCAT(t.id ORDER BY t.score desc),\',\',3),\',\',-1) as id from
test t GROUP BY t.class
好了到现在 已经获取到了一个list
用 in 来完成最后的步骤
SELECT class,score,name FROM test where id in(
SELECT id from
(SELECT t.class, substring_index(GROUP_CONCAT(t.id ORDER BY t.score desc),\',\',1) as id from
test t GROUP BY t.class
union all
SELECT t.class, substring_index(substring_index(GROUP_CONCAT(t.id ORDER BY t.score desc),\',\',2),\',\',-1) as id from
test t GROUP BY t.class
union all
SELECT t.class, substring_index(substring_index(GROUP_CONCAT(t.id ORDER BY t.score desc),\',\',3),\',\',-1) as id from
test t GROUP BY t.class) t2
) ORDER BY class asc,score desc
最终结果
关于mysql函数GROUP_CONCAT
GROUP_CONCAT([DISTINCT] expr [,expr ...] [ORDER BY {unsigned_integer | col_name | expr} [ASC | DESC] [,col_name ...]] [SEPARATOR str_val])
1.例如:
SELECT student_id, GROUP_CONCAT(courses_id) AS courses FROM student_courses WHERE student_id=2 GROUP BY student_id;
+------------+---------+
| student_id | courses |
+------------+---------+
| 2 | 3,4,5 |
+------------+---------+
2.当然分隔符还可以自定义,默认是以“,”作为分隔符,若要改为“|||”,则使用SEPARATOR来指定,例如:
SELECT student_id, GROUP_CONCAT(courses_id SEPARATOR \'|||\') AS courses FROM student_courses WHERE student_id=2 GROUP BY student_id;
+------------+---------+
| student_id | courses |
+------------+---------+
| 2 | 3|||4|||5 |
+------------+---------+
3.除此之外,还可以对这个组的值来进行排序再连接成字符串,例如按courses_id降序来排:
SELECT student_id, GROUP_CONCAT(courses_id ORDER BY courses_id DESC) AS courses FROM student_courses WHERE student_id=2 GROUP BY student_id;
+------------+---------+
| student_id | courses |
+------------+---------+
| 2 | 5,4,3 |
+------------+---------+
4.需要注意的:
a.int字段的连接陷阱
当你用group_concat的时候请注意,连接起来的字段如果是int型,一定要转换成char再拼起来,
否则在你执行后(ExecuteScalar或者其它任何执行SQL返回结果的方法)返回的将不是一个逗号隔开的串,
而是byte[]。
该问题当你在SQLyog等一些工具中是体现不出来的,所以很难发现。
select group_concat(ipaddress) from t_ip 返回逗号隔开的串
select group_concat(id) from t_ip 返回byte[]
select group_concat(CAST(id as char)) from t_dep 返回逗号隔开的串
select group_concat(Convert(id , char)) from t_dep 返回逗号隔开的串
附Cast,convert的用法:
CAST(expr AS type), CONVERT(expr,type) , CONVERT(expr USING transcoding_name)
CAST() 和CONVERT() 函数可用来获取一个类型的值,并产生另一个类型的值。
这个类型 可以是以下值其中的 一个:
BINARY[(N)]
CHAR[(N)]
DATE
DATETIME
DECIMAL
SIGNED [INTEGER]
TIME
UNSIGNED [INTEGER]
b.长度陷阱
用了group_concat后,select里如果使用了limit是不起作用的.
用group_concat连接字段的时候是有长度限制的,并不是有多少连多少。但你可以设置一下。
使用group_concat_max_len系统变量,你可以设置允许的最大长度。
程序中进行这项操作的语法如下,其中 val 是一个无符号整数:
SET [SESSION | GLOBAL] group_concat_max_len = val;
若已经设置了最大长度,则结果被截至这个最大长度。
在SQLyog中执行 SET GLOBAL group_concat_max_len = 10 后,重新打开SQLyog,设置就会生效。
SELECT SUBSTRING(str,pos,len)函数用法
SELECT SUBSTRING(str,pos,len) 返回字符串 str 中以 pos 作为起始位置,长度为 len 的子字符串。
SELECT SUBSTRING(str FROM pos FOR len) -- 返回字符串 str 中以 pos 作为起始位置,长度为 len 的子字符串。
SELECT SUBSTRING(str FROM pos) -- 返回字符串 str 中以 pos 作为起始位置,到结束的子字符串。
SELECT SUBSTRING(str,pos) -- 返回字符串 str 中以 pos 作为起始位置,到结束的子字符串。
SELECT SUBSTRING(str,pos,len) -- 返回字符串 str 中以 pos 作为起始位置,长度为 len 的子字符串。
应用实例:
SELECT SUBSTRING(\'123456\' FROM 2 FOR 3)
------------------------------------------
234
SELECT SUBSTRING(\'123456\',2,3)
--------------------------------------
234
SELECT SUBSTRING(\'123456\' FROM 2)
-----------------------------------
23456
SELECT SUBSTRING(\'123456\',2)
-----------------------------------
23456
以上是关于mysql GROUP_CONCAT+ GROUP BY + substring_index获取分组的前几名的主要内容,如果未能解决你的问题,请参考以下文章