个人博客,记录知识防止遗忘
mysql的group_concat函数与substring_index函数使用举例
mysql的group_concat函数与substring_index函数使用举例

mysql的group_concat函数与substring_index函数使用举例

mysql日常查询时,经常会需要在group by分组逻辑中再“嵌套”一层分组,从而更好的展示数据。
但是group by 本身并不支持嵌套。。。幸好有另一个函数group_concat() 可以解决这个问题。
再搭配字符分隔截取函数 substring_index(),可以很好的展示想要的数据

为了方便理解, 我构建了一些数据用于测试,并模拟了一个简单需求, 用来说明2个函数的用法,若嫌麻烦,可以直接到 “3 解决过程” 中查看使用示例


内容大纲

1    测试表构建与数据导入

1.1    表结构

image.png

1.2    测试表创建语句

DROP TABLE IF EXISTS test_tel;
CREATE TABLE test_tel  (
  id int(11) NOT NULL AUTO_INCREMENT COMMENT '自增id',
  dest_number varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '号码',
  province varchar(30) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '省份',
  PRIMARY KEY (id) USING BTREE
) ENGINE = MyISAM AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

1.3    测试数据导入

(手机号码皆为测试数据,不具有真实性)

INSERT INTO test_tel (dest_number,province) VALUES ('19946250000','上海'),('18918150000','上海'),('18019370000','上海'),('18016000000','上海'),('18930450000','上海'),('18021000000','上海'),('17717350000','上海'),('15316660000','上海'),('17301780000','上海'),('19946220000','上海'),('18087060000','云南'),('15394990000','云南'),('13330500000','云南'),('17787060000','云南'),('18182970000','云南'),('18987760000','云南'),('13312550000','云南'),('18087720000','云南'),('18187320000','云南'),('15398640000','云南'),('18911490000','北京'),('17710230000','北京'),('18910030000','北京'),('15300220000','北京'),('15311410000','北京'),('13381100000','北京'),('18911920000','北京'),('17718410000','北京'),('18001270000','北京'),('15311410000','北京'),('17790050000','吉林'),('18043560000','吉林'),('17390950000','吉林'),('18946630000','吉林'),('18943300000','吉林'),('13331640000','吉林'),('15330600000','吉林'),('18946680000','吉林'),('15354610000','吉林'),('18043120000','吉林'),('19983860000','四川'),('18090410000','四川'),('17302880000','四川'),('15308290000','四川'),('18090430000','四川'),('13340710000','四川'),('18981160000','四川'),('17781770000','四川'),('18081190000','四川'),('18190860000','四川'),('18002080000','天津'),('13302120000','天津'),('18002080000','天津'),('17720160000','天津'),('13370300000','天津'),('15302090000','天津'),('15320160000','天津'),('13389930000','天津'),('18002080000','天津'),('18920330000','天津'),('18995290000','宁夏'),('18152460000','宁夏'),('18195480000','宁夏'),('17395000000','宁夏'),('17711840000','宁夏'),('17752410000','宁夏'),('18195520000','宁夏'),('17309500000','宁夏'),('18169560000','宁夏'),('18169090000','宁夏'),('18956600000','安徽'),('13335660000','安徽'),('18956620000','安徽'),('18905660000','安徽'),('18056610000','安徽'),('13399670000','安徽'),('18019990000','安徽'),('13339280000','安徽'),('18130070000','安徽'),('18956620000','安徽'),('15315800000','山东'),('15345450000','山东'),('17753250000','山东'),('13375500000','山东'),('17753850000','山东'),('13361390000','山东'),('13396260000','山东'),('18105340000','山东'),('15376170000','山东'),('15315880000','山东'),('19135120000','山西'),('13383590000','山西'),('15383470000','山西'),('18035130000','山西'),('18003510000','山西'),('18035990000','山西'),('18903510000','山西'),('13393580000','山西'),('18903590000','山西'),('18903430000','山西'),('18022150000','广东'),('15362150000','广东'),('13326660000','广东'),('15398820000','广东'),('18024080000','广东'),('17765920000','广东'),('18188600000','广东'),('18928090000','广东'),('18925360000','广东'),('18988590000','广东'),('18176290000','广西'),('19978120000','广西'),('13367660000','广西'),('17777570000','广西'),('18977000000','广西'),('19148050000','广西'),('19978740000','广西'),('18078480000','广西'),('18978280000','广西'),('18174990000','广西')

导入数据举例:
image.png

2    需求与分析

需求:
在所有省份中各自随机抽取3个号码,列举出来

分析:
日常使用group by分组时,字段若要参与,一般有2种方式:

  • ①要么将字段加入group by内部作为分组的依据,
  • ②要么对该字段使用聚合函数处理

若采用①的方式, 则省份和号码都会成为分组依据, 与需求矛盾 ( 需求中只需要 "省份’ 这一个分组依据)
若采用②的方式, 则每个省对应的号码无法全部列出, 只能被聚合

3    解决过程:

可以在上述②的基础上进行改进, 使用group_concat 函数,将多行号码"聚合"为一行字符串进行展示

sql如下:

SELECT
    province,
  group_concat( dest_number ORDER BY rand()) AS result 
FROM
    test_tel 
GROUP BY
    province;

执行结果
image.png
此时虽然实现了简单的分组展示需求, 但是仍然没有解决随机抽取的问题
于是需要用到 substring_index 函数对上述这一长串号码进行分段截取的处理

sql如下:

SELECT
    province,
    substring_index( group_concat( dest_number ORDER BY rand( ) ), ',', 3 ) AS result 
FROM
    test_tel 
GROUP BY
    province;

执行结果:
(由于使用随即排序, 每次结果都可能不相同)
image.png

至此,需求都已实现,
此时再回过头简单介绍下2个函数的使用方法

4    group_concat函数

group_concat其实就是将分组后, 对各组内部的"多行数据(逻辑上)"进行处理, 拼接成"一行数据",并最终展示出来

语法:group_concat( [distinct] 要连接的字段 [order by 排序字段 asc/desc ] [ ‘分隔符’] )
分隔符若不指定,则缺省为一个逗号。

上述例子中, 使用group_concat 针对各分组(省份)内部的"多行手机号码", 使用order by rand()随机排序后,由默认的分隔符拼接成"一行数据"

5    substring_index函数

substring_index函数其实就是个按照关键字截取字符串的函数, 日常很实用

语法: substring_index(str, separator,return)
str : 带截取的字段
Separator:分隔符,函数将根据此字符,将str分割成数段
return: 需要返回多少段数据 ,
若return>0, 则从左往右计数
若return<0, 则从右往左计数

例如之前例子中,使用substring_index将group_concat处理后的一整行数据作为待操作数据,使用逗号分隔,最后返回前三个号码(从左往右计数)

6    需求拓展

统计出测试表中号码前3位分别有哪些省份,并按照省份名排序

sql:

SELECT
    left(dest_number,3) AS '号码前3位',
    group_concat( distinct province  ) AS '省份'
FROM
    test_tel 
GROUP BY
    left(dest_number,3)

执行结果
image.png

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注