首先我们来建立一个测试的表和数据,代码如下
CREATE TABLE `per ` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, `pname` varchar (50 ) DEFAULT NULL , `page` int (11 ) DEFAULT NULL , `psex` varchar (50 ) DEFAULT NULL , `paddr` varchar (50 ) DEFAULT NULL , PRIMARY KEY (`id`) ) ENGINE= InnoDB AUTO_INCREMENT= 13 DEFAULT CHARSET= utf8mb4; INSERT INTO `per ` VALUES ('1' , '王小华' , '30' , '男' , '北京' );INSERT INTO `per ` VALUES ('2' , '张文军' , '24' , '男' , '上海' );INSERT INTO `per ` VALUES ('3' , '罗敏' , '19' , '女' , '重庆' );INSERT INTO `per ` VALUES ('4' , '张建新' , '32' , '男' , '重庆' );INSERT INTO `per ` VALUES ('5' , '刘婷' , '26' , '女' , '成都' );INSERT INTO `per ` VALUES ('6' , '刘小亚' , '22' , '女' , '重庆' );INSERT INTO `per ` VALUES ('7' , '王建军' , '22' , '男' , '贵州' );INSERT INTO `per ` VALUES ('8' , '谢涛' , '28' , '男' , '海南' );INSERT INTO `per ` VALUES ('9' , '张良' , '26' , '男' , '上海' );INSERT INTO `per ` VALUES ('10' , '黎记' , '17' , '男' , '贵阳' );INSERT INTO `per ` VALUES ('11' , '赵小丽' , '26' , '女' , '上海' );INSERT INTO `per ` VALUES ('12' , '张三' , null , '女' , '北京' );
concat()函数 首先我们先学一个函数叫concat()函数, 这个函数非常简单
功能:就是将多个字符串连接成一个字符串
语法:concat(字符串1, 字符串2,…) 字符串参数用逗号隔开!
返回值: 结果为连接参数产生的字符串,如果有任何一个参数为null,则返回值为null。
案例 这有一张表
+ | id | pname | page | psex | paddr | + | 1 | 王小华 | 30 | 男 | 北京 | | 2 | 张文军 | 24 | 男 | 上海 | | 3 | 罗敏 | 19 | 女 | 重庆 | | 4 | 张建新 | 32 | 男 | 重庆 | | 5 | 刘婷 | 26 | 女 | 成都 | | 6 | 刘小亚 | 22 | 女 | 重庆 | | 7 | 王建军 | 22 | 男 | 贵州 | | 8 | 谢涛 | 28 | 男 | 海南 | | 9 | 张良 | 26 | 男 | 上海 | | 10 | 黎记 | 17 | 男 | 贵阳 | | 11 | 赵小丽 | 26 | 女 | 上海 | | 12 | 张三 | NULL | 女 | 北京 | +
执行如下语句
select concat(pname,page,psex) from per ;
结果
+ | concat(pname,page,psex) | + | 王小华30 男 | | 张文军24 男 | | 罗敏19 女 | | 张建新32 男 | | 刘婷26 女 | | 刘小亚22 女 | | 王建军22 男 | | 谢涛28 男 | | 张良26 男 | | 黎记17 男 | | 赵小丽26 女 | | NULL | +
为什么会有一条是NULL呢?
那是因为第12条数据中的page字段为空,根据有一个字段为空结果就为NULL的理论推导出 查询出的最后一条记录为NULL!
结果虽然连在一起显示了 但是彼此没有分隔符,因此衍生出来的 concat_ws()
函数
concat_ws()函数 功能:concat_ws()函数 和 concat()函数一样,也是将多个字符串连接成一个字符串,但是可以指定分隔符!
语法:concat_ws(separator, str1, str2, …) 第一个参数指定分隔符, 后面依旧是字符串
separator就是分隔符字符!
需要注意的是分隔符不能为null,如果为null,则返回结果为null。
案例 select concat_ws(',' ,pname,page,psex) from per ; ```` 以逗号分割 结果如下 ```sql + | concat_ws(',' ,pname,page,psex) | + | 王小华,30 ,男 | | 张文军,24 ,男 | | 罗敏,19 ,女 | | 张建新,32 ,男 | | 刘婷,26 ,女 | | 刘小亚,22 ,女 | | 王建军,22 ,男 | | 谢涛,28 ,男 | | 张良,26 ,男 | | 黎记,17 ,男 | | 赵小丽,26 ,女 | | 张三,女 | +
把分隔符指定为null,结果全部变成了null
select concat_ws(null ,pname,page,psex) from per ; #
+ | concat_ws(null ,pname,page,psex) | + | NULL | | NULL | | NULL | | NULL | | NULL | | NULL | | NULL | | NULL | | NULL | | NULL | | NULL | | NULL | +
group_concat()函数 功能:将group by产生的同一个分组中的值连接起来,返回一个字符串结果。
语法:group_concat( [distinct] 要连接的字段 [order by 排序字段 asc/desc ] [separator ‘分隔符’] )
注意: 中括号[]中的内容是可选的
分析: 通过使用distinct可以排除重复值;如果希望对结果中的值进行排序,可以使用order by子句;separator是一个字符串值,缺省为一个逗号。
重点注意
group_concat只有与group by语句同时使用才能产生效果 所以使用 GROUP_CONCAT()函数必须对源数据进行分组,否则所有数据会被合并成一行
需要将拼接的结果去重的话,可与DISTINCT结合使用即可
案例1 需求: 要查在重庆的有哪些人? 并且把这些人的名字用 ‘-‘ 字符分隔开 然后显示出来, SQL语句如下
# select paddr, group_concat(distinct pname order by pname desc separator '-' ) as '人' from per group by paddr;
# + | paddr | 人 | + | 上海 | 赵小丽- 张良- 张文军 | | 北京 | 王小华- 张三 | | 成都 | 刘婷 | | 海南 | 谢涛 | | 贵州 | 王建军 | | 贵阳 | 黎记 | | 重庆 | 罗敏- 张建新- 刘小亚 | + #
案例2 需求:要查在重庆的有哪些人? 并且把这些人的名字用逗号隔开
以上需求跟上面的案例1 差不多 所以加一个效果, 也就是显示出来的名字前面把id号也加上
# select paddr, group_concat(concat_ws('-' ,id,pname) order by id asc ) as '人' from per group by paddr;
# + | paddr | 人 | + | 上海 | 2 - 张文军,9 - 张良,11 - 赵小丽 | | 北京 | 1 - 王小华,12 - 张三 | | 成都 | 5 - 刘婷 | | 海南 | 8 - 谢涛 | | 贵州 | 7 - 王建军 | | 贵阳 | 10 - 黎记 | | 重庆 | 3 - 罗敏,4 - 张建新,6 - 刘小亚 | +
** 注意:
MySQL中函数是可以嵌套使用的
一般使用group_concat()函数,必须是存在group by 分组的情况下 才能使用这个函数
案例3 准备以下测试数据
准备一个student学生表、MySQL代码如下
# CREATE TABLE `student` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, # `stuName` varchar (22 ) DEFAULT NULL , # `course` varchar (22 ) DEFAULT NULL , # `score` int (11 ) DEFAULT NULL , # PRIMARY KEY (`id`) # ) ENGINE= InnoDB AUTO_INCREMENT= 10 DEFAULT CHARSET= utf8; #
插入以下数据
INSERT INTO `student`(stuName,course,score) VALUES ('张三' , '语文' , '91' );INSERT INTO `student`(stuName,course,score) VALUES ('张三' , '数学' , '90' );INSERT INTO `student`(stuName,course,score) VALUES ('张三' , '英语' , '87' );INSERT INTO `student`(stuName,course,score) VALUES ('李四' , '语文' , '79' );INSERT INTO `student`(stuName,course,score) VALUES ('李四' , '数学' , '95' );INSERT INTO `student`(stuName,course,score) VALUES ('李四' , '英语' , '80' );INSERT INTO `student`(stuName,course,score) VALUES ('王五' , '语文' , '77' );INSERT INTO `student`(stuName,course,score) VALUES ('王五' , '数学' , '81' );INSERT INTO `student`(stuName,course,score) VALUES ('王五' , '英语' , '89' );
建立好之后 数据如下显示:
mysql> select * from student; + | id | stuName | course | score | + | 10 | 张三 | 语文 | 91 | | 11 | 张三 | 数学 | 90 | | 12 | 张三 | 英语 | 87 | | 13 | 李四 | 语文 | 79 | | 14 | 李四 | 数学 | 95 | | 15 | 李四 | 英语 | 80 | | 16 | 王五 | 语文 | 77 | | 17 | 王五 | 数学 | 81 | | 18 | 王五 | 英语 | 89 | +
继续使用group_concat()函数 加深印象
需求1 以stuName学生名称分组,把得分数score字段的值打印在一行,逗号分隔(默认)
select stuName, GROUP_CONCAT(score) as '当前这个学生的得分数' from student GROUP BY stuName;
运行结果
+ | stuName | 当前这个学生的得分数 | + | 张三 | 91 ,90 ,87 | | 李四 | 79 ,95 ,80 | | 王五 | 77 ,81 ,89 | +
需求2 根据需求1,分数是出来了 但是不知道是什么科目分数 所以还要把科目也连起来显示,并且分数还是从小到大,SQL如下
select stuName, GROUP_CONCAT(concat_ws('=' ,course,score) order by score asc ) as '当前这个学生的得分数' from student GROUP BY stuName;
执行结果
+ | stuName | 当前这个学生的得分数 | + | 张三 | 英语= 87 ,数学= 90 ,语文= 91 | | 李四 | 语文= 79 ,英语= 80 ,数学= 95 | | 王五 | 语文= 77 ,数学= 81 ,英语= 89 | +
需求3 查询出 语文、数学、外语 三门课的最低分,还有哪个学生考的?
简单的说 先连接好分数字段中的得分默认用逗号 再从分数连接字符中提取第一个出来
# # SELECT course,SUBSTRING_INDEX(GROUP_CONCAT(score ORDER BY score ASC ),',' ,1 ) AS score FROM student GROUP BY course;
# + | course | score | + | 数学 | 81 | | 英语 | 80 | | 语文 | 77 | +
# # SELECT g.`id`,g.`course`,g.`score`,g.`stuName`FROM (SELECT course,SUBSTRING_INDEX(GROUP_CONCAT(score ORDER BY score ASC ),',' ,1 ) AS score FROM student GROUP BY course) as t LEFT JOIN student AS g ON (t.course = g.`course` AND t.score = g.`score`) #
# + | id | course | score | stuName | + | 15 | 英语 | 80 | 李四 | | 16 | 语文 | 77 | 王五 | | 17 | 数学 | 81 | 王五 | +
案例4 准备一个商品表, 代码如下
# CREATE TABLE `goods` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, # `price` varchar (22 ) DEFAULT NULL , # `goods_name` varchar (22 ) DEFAULT NULL , # PRIMARY KEY (`id`) ) ENGINE= InnoDB AUTO_INCREMENT= 1 DEFAULT CHARSET= utf8; # INSERT INTO `goods`(price,goods_name) VALUES (10.00 , '皮包' );INSERT INTO `goods`(price,goods_name) VALUES (20.00 , '围巾' );INSERT INTO `goods`(price,goods_name) VALUES (30.00 , '围巾' );INSERT INTO `goods`(price,goods_name) VALUES (40.00 , '游戏机' );INSERT INTO `goods`(price,goods_name) VALUES (60.00 , '皮包' );INSERT INTO `goods`(price,goods_name) VALUES (80.00 , '游戏机' );INSERT INTO `goods`(price,goods_name) VALUES (220.00 , '游戏机' );INSERT INTO `goods`(price,goods_name) VALUES (780.00 , '围巾' );INSERT INTO `goods`(price,goods_name) VALUES (560.00 , '游戏机' );INSERT INTO `goods`(price,goods_name) VALUES (30.00 , '皮包' );
需求1 以商品名称分组,把price字段的值在一行打印出来,分号分隔
select goods_name,group_concat(price) from goods group by goods_name;
需求2 以商品名称分组,把price字段的值在一行打印出来,分号分隔 并且去除重复冗余的价格字段的值
select goods_name,group_concat(distinct price) from goods group by goods_name;
需求3 以商品名称分组,把price字段的值在一行打印出来,分号分隔 去除重复冗余的价格字段的值 并且排序 从小到大
select goods_name,group_concat(distinct price order by price desc ) from goods group by goods_name; #select goods_name,group_concat(distinct price order by price+ 1 desc ) from goods group by goods_name; ##
案例5 使用group_concat()函数来做一个多表查询
准备三张 测试数据表: 用户表[user]、水果表[fruit]、用户喜欢哪些水果的表[user_like]
# CREATE TABLE `user ` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, # `username` varchar (22 ) DEFAULT NULL , # PRIMARY KEY (`id`) ) ENGINE= InnoDB AUTO_INCREMENT= 1 DEFAULT CHARSET= utf8; # # INSERT INTO `user `(username) VALUES ('张三' );INSERT INTO `user `(username) VALUES ('李四' );INSERT INTO `user `(username) VALUES ('王文玉' );
# CREATE TABLE `fruit` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, # `fruitname` varchar (22 ) DEFAULT NULL , # PRIMARY KEY (`id`) ) ENGINE= InnoDB AUTO_INCREMENT= 1 DEFAULT CHARSET= utf8; # # INSERT INTO `fruit`(fruitname) VALUES ('西瓜' ); INSERT INTO `fruit`(fruitname) VALUES ('苹果' ); INSERT INTO `fruit`(fruitname) VALUES ('芒果' ); INSERT INTO `fruit`(fruitname) VALUES ('梨' ); INSERT INTO `fruit`(fruitname) VALUES ('葡萄' );
# CREATE TABLE `user_like` ( `id` int (11 ) NOT NULL AUTO_INCREMENT, # `user_id` int , # `fruit_id` int , # CONSTRAINT user_like PRIMARY KEY (id,user_id,fruit_id) # ) ENGINE= InnoDB AUTO_INCREMENT= 1 DEFAULT CHARSET= utf8; # # INSERT INTO `user_like`(user_id,fruit_id) VALUES (1 ,1 ); # INSERT INTO `user_like`(user_id,fruit_id) VALUES (1 ,2 ); # INSERT INTO `user_like`(user_id,fruit_id) VALUES (1 ,3 ); # INSERT INTO `user_like`(user_id,fruit_id) VALUES (2 ,3 ); # INSERT INTO `user_like`(user_id,fruit_id) VALUES (2 ,4 ); # INSERT INTO `user_like`(user_id,fruit_id) VALUES (2 ,5 ); # INSERT INTO `user_like`(user_id,fruit_id) VALUES (3 ,5 ); # INSERT INTO `user_like`(user_id,fruit_id) VALUES (3 ,1 ); # INSERT INTO `user_like`(user_id,fruit_id) VALUES (3 ,2 ); #
需求 查出每个用户喜欢的水果都有哪些
# select u.username,group_concat(f.fruitname) from user_like as c inner join user as u on c.user_id= u.id inner join fruit as f on c.fruit_id= f.id group by c.user_id;
# + | username | group_concat(f.fruitname) | + | 张三 | 芒果,苹果 | | 李四 | 梨,芒果,葡萄 | | 王文玉 | 西瓜,葡萄,苹果 | +