mysql GROUP_CONCAT+ GROUP BY + substring_index获取分组的前几名

  • Post author:
  • Post category:mysql


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