游乐游手机版
首页/数据库/文章详情

MySQL中concat_ws与group_concat函数的区别对比

时间:2026-07-25 19:14
MySQL中concat函数连接字符串时若任一参数为null则返回null;concat_ws可指定分隔符,分隔符为null时结果也为null;group_concat用于分组内多行拼接,支持去重、排序和自定义分隔符,但数值型需转为字符型。三者各有适用场景,需注意null值处理。

一、concat() 函数

先来看一个最常用的场景:当我们需要将多个字段拼接成一个字符串时,该如何操作?MySQL 提供了三个函数来解决这类问题:concatconcat_wsgroup_concat。它们各自有明确的适用场景,如果选错函数反而会导致结果异常。

concat() 的功能非常直接——将多个字符串连接成一个字符串。语法是 concat(str1, str2, …),返回结果为连接参数产生的字符串。但有一个容易踩的坑:如果任何一个参数为 null,则整个返回值就是 null

举例1

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

select CONCAT(id,name,email) as userinfo from users;

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

结果中有一行显示为 null,原因就是 users 表中那行的 email 值为 null。因此使用 concat 时,务必先确认拼接的字段里没有 null 值,否则整条记录都会变成空值,前功尽弃。

举例2

上面的结果里,三个字段(id、name、email)直接拼在一起,没有任何分隔符,看起来非常不直观。加上逗号作为分隔符后效果会好很多:

select CONCAT(id,',',name,',',email) as userinfo from users;

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

二、concat_ws() 函数

每次使用 concat 都需要手动在每个字段之间插入逗号,如果字段较多会非常繁琐。于是 concat_ws() 应运而生——con cat with separator,可以一次性指定分隔符,大大简化了操作。

语法:concat_ws(separator, str1, str2, …)。第一个参数就是分隔符。注意:分隔符不能为 null,如果设置为 null,结果会直接返回 null

举例

concat_ws() 将分隔符指定为逗号:

select CONCAT_WS(',',id,name,email) as userinfo from users;

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

至于为什么把分隔符写成 null 后结果全部变成 null,建议你亲自动手测试一下,来验证这个特性。

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

三、group_concat() 函数

前面两个函数都是在单行内拼接字段,但在实际工作中经常需要将多行数据按某个分组归并到一起。例如,按姓名分组,把同一个人的所有 ID 拼成一行。这时候 group_concat() 就派上用场了。

先回顾一下基本规则:在有 group by 的查询中,select 指定的字段要么包含在 group by 后面,要么包含在聚合函数中。否则会报错或者结果不准确。

举例1

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

这个查询查的是 name 相同的人中最小的 id。如果我们要查 name 相同的人所有的 id 呢?当然可以这样:

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

但这样同一个名字会出现多次,数据冗余,而且不够直观。有没有更优雅的方法?——使用 group_concat()

group_concat() 的功能:将 group by 产生的同一个分组中的值连接起来,返回一个字符串结果。

语法:

group_concat([DISTINCT] 要连接的字段  [Order BY ASC/DESC 排序字段]  [Separator '分隔符'])

通过 distinct 可以排除重复值;通过 order by 可以对结果进行排序;separator 用来指定分隔符,缺省为逗号。

举例2

group_concat() 配合 group by 显示相同名字的人的 id 号:

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

还可以把 id 从大到小排序,并用 _ 作为分隔符:

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

上面只显示了 id,如果还想同时显示每个分组内的 email 呢?

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

示例

GROUP_CONCAT([DISTINCT] expr [,expr ...]             [ORDER BY {unsigned_integer | col_name | expr}                 [ASC | DESC] [,col_name ...]]             [SEPARATOR str_val])

测试数据

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

1. 查询所有学生的选课信息

SELECT    s.stu_id         AS  studentId,    s.stu_name       AS  studentName,    c.course_id      AS  courseId,    c.course_name    AS  studentCourseFROM    student sLEFT JOIN    stu_course scON    s.stu_id = sc.stu_idLEFT JOIN    course cON    sc.course_id = c.course_id

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

2 使用 GROUP_CONCAT() 函数将结果分组归集

SELECT    s.stu_id       AS  studentId,    s.stu_name     AS  studentName,    GROUP_CONCAT(c.course_id)    AS  courseId,    GROUP_CONCAT(c.course_name)  AS  studentCourseFROM    student sLEFT JOIN    stu_course scON    s.stu_id = sc.stu_idLEFT JOIN    course cON    sc.course_id = c.course_idGROUP BY    studentId

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

注意:使用 GROUP_CONCAT 时,数值类型的数据会被转化成二进制 BLOB 类型,这可能导致显示异常。稳妥的做法是用 CAST(expr AS type)CONVERT(expr, type) 将数值转成字符串:

SELECT    s.stu_id          AS  studentId,    s.stu_name     AS  studentName,    GROUP_CONCAT(CAST(c.course_id AS CHAR))     AS  courseId,    GROUP_CONCAT(c.course_name)  AS  studentCourseFROM    student sLEFT JOIN    stu_course scON    s.stu_id = sc.stu_idLEFT JOIN    course cON    sc.course_id = c.course_idGROUP BY    studentId

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

3 若要将第一条记录中 courseId 按照从小到大的方式归集

可以在 GROUP_CONCAT() 里加上 ORDER BY

SELECT    s.stu_id          AS  studentId,    s.stu_name     AS  studentName,    GROUP_CONCAT(CAST(c.course_id AS CHAR) ORDER BY c.course_id) AS courseId,    GROUP_CONCAT(c.course_name)  AS  studentCourseFROM    student sLEFT JOIN    stu_course scON    s.stu_id = sc.stu_idLEFT JOIN    course cON    sc.course_id = c.course_idGROUP BY    studentId

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

不过这里有一个小遗憾:虽然 courseId 这一列按从小到大排序了,但另一列 studentCourse 对应的数据并没有跟着一起变化。这算是 GROUP_CONCAT 的一个局限——各字段的排序是独立进行的,无法联动。如果哪位知道怎么让另一列也跟着变,不妨分享一下经验。

4 改变分隔符

GROUP_CONCAT 默认分隔符是逗号,用 SEPARATOR 关键字可以换成其他符号:

SELECT    s.stu_id       AS  studentId,    s.stu_name     AS  studentName,    GROUP_CONCAT(CAST(c.course_id AS CHAR) ORDER BY c.course_id SEPARATOR '/')    AS  courseId,    GROUP_CONCAT(c.course_name SEPARATOR '|')   AS  studentCourseFROM    student sLEFT JOIN    stu_course scON    s.stu_id = sc.stu_idLEFT JOIN    course cON    sc.course_id = c.course_idGROUP BY    studentId

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

数据库

CREATE TABLE `users` (  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,  `name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,  `email` varchar(255) CHARACTER SET utf8 DEFAULT NULL,  `email_verified_at` timestamp NULL DEFAULT NULL,  `password` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,  `remember_token` varchar(100) COLLATE utf8mb4_unicode_ci DEFAULT NULL,  `created_at` timestamp NULL DEFAULT NULL,  `updated_at` timestamp NULL DEFAULT NULL,  `deleted_at` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,  PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=50078 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;INSERT INTO `users` (`id`, `name`, `email`, `email_verified_at`, `password`, `remember_token`, `created_at`, `updated_at`, `deleted_at`) VALUES ('77', '', NULL, NULL, '', NULL, NULL, NULL, NULL);INSERT INTO `users` (`id`, `name`, `email`, `email_verified_at`, `password`, `remember_token`, `created_at`, `updated_at`, `deleted_at`) VALUES ('5577', '', '55@qq.com', NULL, '', NULL, NULL, NULL, NULL);INSERT INTO `users` (`id`, `name`, `email`, `email_verified_at`, `password`, `remember_token`, `created_at`, `updated_at`, `deleted_at`) VALUES ('50010', 'willem', '11@qq.com', NULL, '', NULL, NULL, NULL, '2022-04-07 14:03:11');INSERT INTO `users` (`id`, `name`, `email`, `email_verified_at`, `password`, `remember_token`, `created_at`, `updated_at`, `deleted_at`) VALUES ('50012', 'wgchen', '22@qq.com', NULL, '', NULL, NULL, NULL, '2022-04-07 14:03:16');INSERT INTO `users` (`id`, `name`, `email`, `email_verified_at`, `password`, `remember_token`, `created_at`, `updated_at`, `deleted_at`) VALUES ('50018', 'ycc', '33@qq.com', NULL, '', NULL, NULL, NULL, '2022-04-07 14:03:23');INSERT INTO `users` (`id`, `name`, `email`, `email_verified_at`, `password`, `remember_token`, `created_at`, `updated_at`, `deleted_at`) VALUES ('50077', '77', '', NULL, '', NULL, NULL, NULL, '2022-04-07 14:06:39');

案例

1 MySQL 中某个字段前或后添加字符

UPDATE optional_productSET cate_path = concat('|',cate_path,'|')

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

2 MySQL 中批量替换某个字段的值:replace

例如,表 mxd_goods 中字段 goods_pic 的值需要全部去掉前缀 goods/

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

执行以下语句:

update  mxd_goods set goods_pic= replace(goods_pic, 'goods/', '');

注意:一定要加上 where id = 数字,否则就是全表修改,后果非常严重。

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

那怎么撤销这次修改呢?

UPDATE mxd_goodsSET goods_pic = REPLACE (goods_pic, 'g_', 'goods/g_')WHEREgoods_id = 1849;

3 substring(a,b,c) 截取字符串

substring(a,b,c):第一个参数 a 是被截取的字符串,第二个参数 b 是起始位置(从1开始),第三个参数 c 是要截取的长度。

举例:

SELECT  SUBSTRING("hello",2,2);

结果是从 'hello' 的第二个字符 'e' 开始截取两个字符,得到 "el"。

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

4 MySQL 删除最后一个字符

如果想只删除最后一个字符(比如移除多余的逗号),用 SUBSTRING 只能显示修改后的结果,不会更新实际数据。如果需要更新列的值,可以用 CONCAT(LEFT(…), '') 的方式:

UPDATE SET  = CONCAT(LEFT(, CHAR_LENGTH() -1), '')WHERE ;

也可以用 SUBSTR 配合 CHAR_LENGTH 来查询:

SELECTSUBSTR(title,1,CHAR_LENGTH(title) - 1)FROMhhir_crm_email_send_contentWHEREid = 14;

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

项目案例

SELECT`record`.`id`,`record`.`fund_name`,`record`.`report_type`,`record`.`number`,`record`.`year`,`record`.`quarter`,`record`.`notice_time`,`record`.`customer_sub_name`,`record`.`legal_subject_name`,`record`.`send_status`,`record`.`created_at`,`report_send`.`report_name`,`send_content`.`title` AS `email_title`,GROUP_CONCAT(`contact_link`.`contact_name`,concat('(', `contact_link`.`contact_email`, ');') SEPARATOR '') AS contactsFROM`hhir_crm_email_record` AS `record`LEFT JOIN `hhir_crm_email_report_send` AS `report_send` ON `report_send`.`email_record_id` = `record`.`id`AND `report_send`.`valid` = 1LEFT JOIN `hhir_crm_email_send_content` AS `send_content` ON `send_content`.`email_record_id` = `record`.`id`AND `send_content`.`valid` = 1LEFT JOIN `hhir_crm_email_contact_link` AS `contact_link` ON `contact_link`.`email_record_id` = `record`.`id`AND `contact_link`.`valid` = 1WHERE`record`.`valid` = 1GROUP BY `record`.`id`

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

SELECTid,GROUP_CONCAT(title,concat('(', cc_emails, ');') SEPARATOR '') AS email_titleFROMhhir_crm_email_send_contentWHEREid IN (12, 13)

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

MySQLconcat_ws和group_concat函数有什么区别?一文搞懂

总结

三个函数各有侧重:concat 适合简单拼接,但需要留意 null 值;concat_ws 更加简洁优雅,省去了重复写分隔符的麻烦;group_concat 则是分组聚合的利器,尤其适合处理一对多关系的数据归并。掌握它们之后,日常的字符串处理工作会变得顺手很多。

来源:https://www.jb51.net/database/368045vtc.htm
上一篇SpringBoot整合Redis哨兵集群的高并发解决方案 下一篇MySQL外键约束中RESTRICT与NOACTION两种模式有何不同全面详解
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
自增主键值从何而来?深入理解原理,告别只会auto_increment
数据库 · 2026-07-25

自增主键值从何而来?深入理解原理,告别只会auto_increment

KingbaseES推荐使用serial、bigserial、显式sequence或identity列实现自增主键。serial创建integer并关联序列,bigserial对应bigint;显式sequence可自定义起始值等参数;identity有generatedbydefault(允许指定值)与always(禁止)两种模式。

Linux下瀚高数据库授权文件过期及替换解决方案
数据库 · 2026-07-25

Linux下瀚高数据库授权文件过期及替换解决方案

在银河麒麟系统下,瀚高数据库hgdb-4 5试用授权20天到期后需替换正式授权文件。正确操作:停止服务,备份旧文件,将授权文件复制到 opt highgo hgdb-4 5 etc lic 并命名为hgdb lic,设置权限600和属主highgo:highgo,再启动服务。禁止直接修改data目录下的license info文件。

Oracle BLOB实时同步的5大技术挑战与难点解析
数据库 · 2026-07-25

Oracle BLOB实时同步的5大技术挑战与难点解析

OracleBLOB实时同步面临分片组装、多列隔离、长事务跨窗口、事务回滚及大对象资源控制等技术挑战,必须在日志中精确还原完整字段值,才能保证源端与目标端数据完全一致,这对同步系统的稳健性提出了高要求。

MySQL禁用redo日志导致全备失败
数据库 · 2026-07-25

MySQL禁用redo日志导致全备失败

MySQL全量备份失败是由于数据定义语言操作触发排序索引构建,禁用重做日志导致XtraBackup无法获取一致性备份。测试验证表明,优化表语句即使无数据也会触发该问题。根本原因在于排序索引构建过程跳过了重做日志记录,破坏了备份的一致性。

Kafka架构图优化与改进的全面详细步骤与实践指南
数据库 · 2026-07-25

Kafka架构图优化与改进的全面详细步骤与实践指南

Kafka作为实时数据流处理的核心中间件,其底层架构虽已相当成熟,但在实际生产环境中,要充分发挥其性能潜力,仍需落实到具体的调优与架构改造上。核心目标可归纳为三点:如何承载更高的吞吐量、如何保障数据不丢失、以及故障发生时如何快速恢复。本文将从这几个关键方向出发,深入探讨如何真正榨干Kafka集群的性