MySQL中concat函数连接字符串时若任一参数为null则返回null;concat_ws可指定分隔符,分隔符为null时结果也为null;group_concat用于分组内多行拼接,支持去重、排序和自定义分隔符,但数值型需转为字符型。三者各有适用场景,需注意null值处理。
在日常开发中,经常需要将多个字段拼接成一个字符串。MySQL 提供了三个函数来解决这类问题:concat、concat_ws 和 group_concat。每个函数都有各自适用的场景,使用不当可能带来意料之外的问题。
concat() 的功能非常直接——将多个字符串连接成一个字符串。语法格式为 concat(str1, str2, …),返回结果为连接参数后生成的字符串。但有一个需要特别注意的地方:如果其中任何一个参数为 null,那么整个返回值都会变成 null。
长期稳定更新的攒劲资源: >>>点此立即查看<<<

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

结果中有一行显示为 null,原因在于 users 表中该行的 email 值为 null。因此使用 concat 时,务必确认拼接的字段中不含 null,否则整条记录的结果都将失效。
上面的结果中,三个字段(id、name、email)直接拼接在一起,没有分隔符,可读性较差。加入逗号作为分隔符后效果会好很多:
select CONCAT(id,',',name,',',email) as userinfo from users;

每次使用 concat 都需要手动在每个字段之间添加逗号,字段数量较多时操作起来比较繁琐。为此 MySQL 提供了 concat_ws()——concat with separator,可以一次性指定分隔符,简化拼接过程。
语法格式:concat_ws(separator, str1, str2, …)。第一个参数即为分隔符。需要特别注意的是:分隔符不能为 null,如果设置为 null,结果会直接返回 null。
使用 concat_ws() 将分隔符指定为逗号:
select CONCAT_WS(',',id,name,email) as userinfo from users;

至于为什么将分隔符设为 null 后结果全部变成 null,可以自行测试验证这一特性。

前两个函数都是在单行内拼接字段,但实际工作中经常需要将多行数据按照某个分组归并到一起。例如,按姓名分组,将同一个人的所有 ID 拼接成一行。这时 group_concat() 就派上了用场。
先回顾一下基本规则:在使用 group by 的查询中,select 指定的字段要么包含在 group by 后面,要么包含在聚合函数中,否则会导致报错或结果不准确。

这个查询返回的是 name 相同的人中最小的 id。如果要查询 name 相同的人所有的 id,可以这样写:

但这样同一个名字会出现多次,数据冗余且不够直观。有没有更优雅的方式?——使用 group_concat()。
group_concat() 的功能:将 group by 产生的同一分组中的值连接起来,返回一个字符串结果。
语法格式:
group_concat([DISTINCT] 要连接的字段 [Order BY ASC/DESC 排序字段] [Separator '分隔符'])
通过 distinct 可以去除重复值;通过 order by 可以对结果排序;separator 用于指定分隔符,默认值为逗号。
使用 group_concat() 配合 group by 显示相同名字的人的 id 号:

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

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

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

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

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

需要注意的是:使用 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

可以在 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

这里有一个小细节需要注意:虽然 courseId 这一列已经按从小到大排序,但另一列 studentCourse 对应的数据并没有随之变动。这是 GROUP_CONCAT 的一个局限——各字段的排序是独立进行的,无法联动。如果哪位知道如何让另一列也跟着变动,欢迎分享。
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

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');
UPDATE optional_productSET cate_path = concat('|',cate_path,'|')

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

执行以下语句:
update mxd_goods set goods_pic= replace(goods_pic, 'goods/', '');
注意:务必加上 where id = 数字 条件,否则会进行全表修改,后果严重。

如何撤销修改?
UPDATE mxd_goodsSET goods_pic = REPLACE (goods_pic, 'g_', 'goods/g_')WHEREgoods_id = 1849;
substring(a,b,c):第一个参数 a 为被截取的字符串,第二个参数 b 为起始位置(从1开始计数),第三个参数 c 为截取长度。
示例:
SELECT SUBSTRING("hello",2,2);
结果是从 'hello' 的第二个字符 'e' 开始截取两个字符,得到 "el"。

如果需要删除最后一个字符(例如移除多余的逗号),使用 SUBSTRING 只能显示修改后的结果,不会更新实际数据。如果需要更新列的值,可以使用 CONCAT(LEFT(…), '') 的方式:
UPDATESET = CONCAT(LEFT( , CHAR_LENGTH( ) -1), '')WHERE ;
也可以使用 SUBSTR 配合 CHAR_LENGTH 进行查询:
SELECTSUBSTR(title,1,CHAR_LENGTH(title) - 1)FROMhhir_crm_email_send_contentWHEREid = 14;

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`

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


三个函数各有侧重:concat 适合简单拼接,但需要注意 null 值的影响;concat_ws 更加优雅,省去了重复书写分隔符的麻烦;group_concat 则是分组聚合的利器,特别适合处理一对多关系的数据归并。掌握这三个函数,日常的字符串处理工作会变得更加得心应手。
侠游戏发布此文仅为了传递信息,不代表侠游戏网站认同其观点或证实其描述