news 2026/10/3 9:28:57

MySQL GROUP_CONCAT详解:语法、避坑与性能优化

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
MySQL GROUP_CONCAT详解:语法、避坑与性能优化

做MySQL开发和运维的同学,应该都有过这种经历:一张订单表配一张订单商品明细表,业务上要展示“订单下所有商品的编号”,如果不用函数,只能在应用层写循环嵌套查询,或者在DAO层一次性查出所有明细再分组拼接。我第一次遇到这种需求时,写了一大段Java代码,循环里还做了排序去重,又慢又难看。后来看到研发同事的SQL里冒出个GROUP_CONCAT(),一行函数直接解决,当时就觉得这函数是真的香。这篇内容想把GROUP_CONCAT()的语法细节、实战用法和容易踩的坑一次说清楚,适合写SQL的研发、做报表的数据分析师,以及天天跟MySQL打交道的运维同学,哪怕你只是面试前突击MySQL,也能从里面捞到不少干货。

1. 项目概述与函数定位

1.1 GROUP_CONCAT到底解决什么问题

GROUP_CONCAT()是MySQL里一个标量聚合函数,作用就是把同一个分组里的多行数据,按照你指定的顺序和分隔符,拼成一个字符串返回。本质上它做的就是“行转列”中的一类场景:多行转一列。与之对比的普通聚合函数 COUNT、SUM、AVG,结果都是数值,GROUP_CONCAT的结果是文本,所以它在聚合后还能保留原始数据的“存在感”。

举个例子,有一张学生选课表:

SELECT student_name, GROUP_CONCAT(course_name) FROM student_course GROUP BY student_name;

这是最典型的用法:把同一个学生的所有课程名拼在一行里。你没用它之前,可能先把所有记录查出来丢给应用层,自己维护学生ID和课程列表的Map,循环拼接;用了它之后,数据库一次扫描、一次分组加拼接,SQL写完即是结果,配合报表导出非常顺手。

GROUP_CONCAT解决的另一个核心需求是“冗余展示”。比如一个部门对应多个员工,一个分类对应多篇文章,业务详情页需要显示一批关联记录的ID或名称,与其多查一次并额外维护缓存,不如在SQL里直接拼接好。这也是许多日报、周报里经常看到它身影的原因。

实际工作中,我见过不少开发同学对聚合函数的理解停留在SUM、COUNT上,碰到“把多个值合并成一个字段”的需求时,第一反应是在Java或PHP里拼接。不是说应用层拼接不行,而是当数据量上来以后,一次查询出一条主记录、再循环查明细,会产生 N+1 查询问题,接口响应时间直线上升。GROUP_CONCAT让数据库在分组阶段就把数据压平,减少了一次往返,尤其在报表查询这种“一次性拿全量”的场景里,优势很明显。

1.2 函数语法与基本参数拆解

直接看官方语法:

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

这个语法看着唬人,拆开其实就四个部分:

  • DISTINCT:可选,对拼接前的值去重。注意它去重的粒度是整个表达式,不是只针对第一个列。
  • 表达式:可以是一个字段,也可以是字符串拼出来的列,比如CONCAT(module, '-', action),多个表达式之间用逗号分隔。
  • ORDER BY:可选,控制拼接顺序。注意这里只能在内部排序,不能像平时一样写ORDER BY 别名,写字段名或数字位置都可以。
  • SEPARATOR:可选,分隔符,默认是英文逗号。我见过有人以为默认是空格,结果字符串糊成一团,其实默认就是,。

一个最基础的实战例子:

SELECT class_id, GROUP_CONCAT(DISTINCT student_name ORDER BY student_id DESC SEPARATOR '、') AS students FROM class_student WHERE class_id = 1001 GROUP BY class_id;

这个语句干了三件事:同一个班级的学生去重、按学号倒序排列、用中文顿号拼接。别小看这些细节,很多线上数据的展示效果差异,都来自这里。

提示:GROUP_CONCAT必须配合GROUP BY使用才有“分组聚合”的意义。如果 SQL 里没有GROUP BY,MySQL 会把整张表当成一个组处理,有时候这不是你想要的结果。

关于多个表达式,有一个容易忽略的点:GROUP_CONCAT(a, b)其实等价于GROUP_CONCAT(CONCAT(a, b)),但两者在语义上有一点点区别。直接写多个表达式时,MySQL 会先按逗号拼出一个完整字符串,再用SEPARATOR连接各行的结果。如果你本身的值里就带逗号,建议用CONCAT显式组装,避免歧义。

2. 核心细节解析与实操要点

2.1 分隔符、排序与去重:三个最常用参数的坑

分隔符不是随便选的。默认逗号有很多场景会出问题:比如拼接的字段本身包含逗号,像地址、标签、带逗号的备注,结果洗数据时就尴尬了。所以我通常直接用竖线|或者中文顿号、。在报表导出时,如果你后续要把拼接结果复制到 Excel 分列,竖线比逗号更不容易撞车。还有一个隐藏知识点:SEPARATOR后面可以跟空字符串,比如SEPARATOR '',常用于拼固定格式,比如SELECT GROUP_CONCAT(id SEPARATOR '')把编号连成一串纯数字。

ORDER BY 排序的坑也值得单独说。GROUP_CONCAT内部的 ORDER BY 只能对参与聚合的列排序,如果你想先按课程结果集已有的排序方式,再把数据拼出来,很可能会发现顺序是乱的。原因是 MySQL 在聚合阶段做排序,跟外层的 ORDER BY 执行时机不同。最稳妥的写法是把排序逻辑写在GROUP_CONCAT内部,明确告诉数据库“拼接时按这个顺序”。其次,ORDER BY 后面是可以写多个字段的,比如ORDER BY subject ASC, score DESC,这在拼接成绩单时很有用。

我之前在做一个商品标签导出功能时,业务要求标签按“重要程度 + 创建时间”排序。直接在GROUP_CONCAT里写成:

GROUP_CONCAT(tag_name ORDER BY is_important DESC, created_at ASC SEPARATOR ',')

这个写法非常直观,而且不需要在外层排序后做二次处理。你如果只写一个ORDER BY created_at,再指望外层ORDER BY影响拼接顺序,基本是白费功夫,因为聚合结果集和外层结果集是两回事。

DISTINCT 去重的粒度问题。很多人以为GROUP_CONCAT(DISTINCT name, score)是按 name 去重,实际上是按(name, score)整个组合去重,name 相同但 score 不同,会保留两条。如果你只想去掉重复的 name,就得只写GROUP_CONCAT(DISTINCT name)。这在统计用户角色、标签聚合时特别容易出错,我见过小伙伴查了两个小时才找到原因是多写了一个字段。

2.2 长度限制问题:默认1024字符带来的坑

GROUP_CONCAT最大的隐藏杀手是长度限制。MySQL 默认的group_concat_max_len是 1024 字节,也就是说聚合出来的字符串超过 1024 字节,后面的内容会被悄悄截断,而且不报错。它不像语法错误那样立刻暴露,只会导致你看到数据不完整,但是 SQL 还是显示执行成功,这类 bug 在生产上排查起来很折磨人。

查看当前设置:

SHOW VARIABLES LIKE 'group_concat_max_len';

修改方式有三种:

  1. 在会话级别临时调大,适合单次统计任务,断开连接即失效:
    SET SESSION group_concat_max_len = 102400;
  2. 在全局配置里改,影响新连接的会话:
    SET GLOBAL group_concat_max_len = 102400;
  3. 写在配置文件 my.cnf 里,一劳永逸:
    group_concat_max_len = 102400

这里有个计算细节:1024 的单位是字节,不是字符数。如果你的字段是 utf8mb4 编码,一个中文字符最多占 3~4 字节,所以 1024 字节大约只能拼 300 多个中文标签。算容量时别只看字符个数。生产环境如果业务上明确要拼接大量明细,直接把值调到 2048000 的也不少见,代价是内存和网络传输增加,因为拼接结果是在内存里完成的,返回给客户端同样要走网络。

我自己的经验是:先评估业务上最大可能的记录数和字段长度,把group_concat_max_len设成两倍冗余。千万别默认用一个 1024 的配置去拼接上千条业务数据,等发现截断了再上线,回滚成本非常高。另外,调大之后记得让开发团队在同一个配置中心维护一份基线,避免不同环境配置漂移。后面我会专门讲一个我踩过的环境配置不一致的案例。

2.3 与其他聚合函数、子查询的配合

GROUP_CONCAT不只是自己能拼,还可以和表达式配合。常见的是在 ORDER BY 里使用另一个字段来控制顺序,在表达式中使用 CONCAT 组合多个字段:

SELECT user_id, GROUP_CONCAT( CONCAT(role_name, ':', role_code) ORDER BY role_sort ASC SEPARATOR '; ' ) AS roles FROM user_role GROUP BY user_id;

这一段在权限系统里非常实用,直接把角色名称和编码拼成一个自解释字符串,供后端的角色标记或者前端的工具提示使用。

GROUP_CONCAT还可以放进子查询里,比如从一张大表里取出每个用户最新的几条商品ID拼起来,再和主表关联。不过需要注意的是,MySQL 8.0 之前,派生表必须要有别名,这在报错里比较常见——Every derived table must have its own alias。我刚开始写这类嵌套时经常漏别名,直到被报错教做人。

在 8.0 以后,你还有更优雅的替代方案,比如JSON_ARRAYAGG()直接返回 JSON 数组,以及配合窗口函数ROW_NUMBER()先取部分行再聚合。这些替代方案放到后面实战部分细说。

3. 实战案例与核心实现

3.1 经典场景:一对多拼接

实战先从最常用的一对多拼接开始。假设有三张表:部门表dept、员工表employee、部门员工关系表dept_employee。你要生成一张报表,每一行是一个部门,员工姓名按入职时间排列,逗号分隔:

SELECT d.dept_name, GROUP_CONCAT(e.emp_name ORDER BY e.hire_date ASC SEPARATOR ',') AS emp_names FROM dept d LEFT JOIN dept_employee de ON d.id = de.dept_id LEFT JOIN employee e ON de.emp_id = e.id GROUP BY d.id, d.dept_name;

这里有几个细节值得圈出来:GROUP BY为什么要带上d.dept_name?因为在ONLY_FULL_GROUP_BY模式下,select 列表中的非聚合列必须出现在 GROUP BY 里,否则会报错。MySQL 5.7 之后默认开启 ONLY_FULL_GROUP_BY,很多老项目升级后遇到的报错就是这里。如果不希望写一串 GROUP BY,另一种思路是把 dept_name 也改成聚合函数,比如MAX(d.dept_name),但这会掩盖数据的逻辑,我一般不推荐。

如果你担心员工过多导致拼接超长,可以先用GROUP_CONCAT内层的表达式限制条数,或者配合子查询提前截断。不过最直接的还是设置group_concat_max_len和业务对齐,前面说过了。

还有一种常见的变体:只取每个部门入职最早的三个员工姓名。这时我会先开窗打行号:

WITH ranked AS ( SELECT d.dept_name, e.emp_name, ROW_NUMBER() OVER (PARTITION BY d.id ORDER BY e.hire_date ASC) AS rn FROM dept d LEFT JOIN dept_employee de ON d.id = de.dept_id LEFT JOIN employee e ON de.emp_id = e.id ) SELECT dept_name, GROUP_CONCAT(emp_name ORDER BY rn SEPARATOR ',') AS emp_names FROM ranked WHERE rn <= 3 GROUP BY dept_name, rn;

这写法把“取前N个”和“拼接”解耦,逻辑更清晰,执行效率也比直接在大集合上做GROUP_CONCAT再截断好一些。

3.2 进阶场景:多表关联与组合聚合

真实业务里经常需要同时拼接多个维度,比如一个商品要同时展示“所属分类路径”和“标签集合”。这时可以在同一个 SQL 里写两个GROUP_CONCAT,一个用在分类表上,一个用在标签表上。但如果两个维度分别关联同一张主表,而且不是一对多关系,很容易出现笛卡尔积导致重复数据。

举个例子,一个商品goods同时关联了goods_category(一个商品属于多个分类)和goods_tag(一个商品有多个标签),直接两个 LEFT JOIN 后再 GROUP_CONCAT,结果会膨胀:分类数量和标签数量相乘,同一个分类出现多次,同一个标签也出现多次,看起来拼接结果里全是重复项。这种问题最稳妥的办法是先分别聚合,再 JOIN 回主表:

SELECT g.id, gc.category_names, gt.tag_names FROM goods g LEFT JOIN ( SELECT goods_id, GROUP_CONCAT(category_name SEPARATOR '/') AS category_names FROM goods_category GROUP BY goods_id ) gc ON gc.goods_id = g.id LEFT JOIN ( SELECT goods_id, GROUP_CONCAT(tag_name SEPARATOR '|') AS tag_names FROM goods_tag GROUP BY goods_id ) gt ON gt.goods_id = g.id;

这就是典型的“先聚合再关联”思路,也是我在项目里反复强调的一点:多对多场景下,尽量把聚合下沉到子查询里,避免多个一对多 JOIN 叠加后结果爆炸。这个坑在报表统计里最致命,因为数据量一大直接翻几倍,甚至几十倍。你可以在本地用两个小表各 3 条明细做一次实验,两个表 JOIN 后会有 9 行中间结果,再去重拼接,性能和准确性都会受影响。

3.3 性能调优和替代方案

GROUP_CONCAT是聚合操作,它需要在分组内进行排序拼接,如果数据量大,内存开销和临时表的使用都会上来。几个调优点:

  1. 在 GROUP BY 字段上建立索引,减少分组时的临时表开销。
  2. 控制输出长度,预防大字段导致的排序内存暴涨。
  3. 避免GROUP_CONCAT内 DISTINCT 和 ORDER BY 对多个大字段操作,DISTINCT 需要在内存/临时表里做去重判断,字段宽、行数多时性能会明显下降。
  4. 能用数值型标识就不拼长文本,比如拼接ID而不是拼接名称,展示层再映射。

从 MySQL 8.0 开始,官方推荐用JSON_ARRAYAGG替代复杂场景下的GROUP_CONCAT,它直接返回 JSON 数组,没有传统意义上的长度限制,还能保留结构化信息,比如:

SELECT class_id, JSON_ARRAYAGG(student_name) AS student_array FROM class_student GROUP BY class_id;

JSON 数组后续在应用层可以很方便地 parse,不需要自己再 split 字符串。但注意,JSON_ARRAYAGG的排序需要配合子查询或窗口函数,因为它本身不支持 ORDER BY。另外,如果你的下游系统是 Kafka、Flink、ClickHouse 这些,JSON 数组比逗号分隔更容易被解析,这也是越来越多项目转向 JSON 聚合函数的原因。不过,字符串拼接仍然有它不可替代的场景:比如日志里直接拼成一行、导出 CSV 时直接输出文本列,JSON 反而需要二次转换。

窗口函数也能实现类似的“行转列压缩”。如果你只需要每个分组的前 N 条拼接,可以用 ROW_NUMBER 先打行号,再在外层过滤,最后 GROUP_CONCAT。这种写法避免了一次性聚合全量数据,在取“每个用户最近三条操作记录”这类需求里更可控。

4. 常见问题与排查技巧实录

4.1 拼接结果缺失或为空:NULL值的处理

GROUP_CONCAT遇到 NULL 值时会直接跳过,而不是拼成字符串 “NULL”。这一点很多从 Oracle 转过来的同学会不习惯。比如你想把用户的所有备注拼起来,备注字段有 NULL,你会得到一个干净的列表。这通常是好事,但也可能隐藏问题:如果所有值都是 NULL,GROUP_CONCAT返回 NULL,而不是空字符串。做报表时这个小地方容易导致前端显示 null 字样。

处理办法很简单,用 IFNULL 或 COALESCE 包一层:

SELECT user_id, GROUP_CONCAT(IFNULL(remark, '') SEPARATOR ',') AS remarks FROM user_remark GROUP BY user_id;

但注意别毫无取舍地全包 IFNULL,否则空字符串也会被拼进去,连续逗号会让结果很难看。我一般视业务决定:要么过滤掉空字符串,要么把所有 NULL 转成统一占位符。比如你可以先过滤:

GROUP_CONCAT(IFNULL(NULLIF(remark, ''), NULL) SEPARATOR ',')

虽然看起来繁琐,但这样空字符串和 NULL 都会被避掉,结果里不会出现两个连续分隔符。

4.2 分组逻辑不对:ONLY_FULL_GROUP_BY 带来的报错

5.7 以后如果 SELECT 里有非聚合字段,又没出现在 GROUP BY 里,会直接报错:

Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column ...

这时候如果你用的是GROUP_CONCAT聚合其他字段,而 select 的非聚合字段忘记加进 GROUP BY,就会触发这个问题。建议改 SQL 时把非聚合字段全部写进 GROUP BY。也可以用ANY_VALUE()包住想要保留但不想分组的字段,比如ANY_VALUE(user_name),这在一些复杂业务里可以简化分组条件。不过要注意,ANY_VALUE取到的是任意一行,如果你不能接受不确定性,就不要这么做。

我在实际操作中更推荐把所有非聚合列都写进 GROUP BY,原因有两个:一是语义清晰,日后维护的人能一眼看懂分组粒度;二是兼容性更好,换到其他数据库或者迁移到 TiDB、PostgreSQL 时,写法差异更小。ANY_VALUE是 MySQL 自己的妥协方案,能用但别滥用。

4.3 生产环境踩坑:拼接超长、内存不足和字符集问题

我在生产环境遇到最折腾的一次,是查询报表时发现GROUP_CONCAT的结果在部分机器上短一截,另一部分机器正常。排查到最后发现,两个环境group_concat_max_len配置不一致,导致同一份 SQL 在不同环境表现不同。这是典型的“开发环境没压到长度阈值,生产环境数据量大直接踩线”的案例。所以在代码评审时,只要看到GROUP_CONCAT,我会顺手问一句:拼接上限评估过没有?配置调了没有?

还有一个隐蔽问题是字符集。如果GROUP_CONCAT内部拼接的字段来自不同的表,而两表的字符集或排序规则不一致,可能报Illegal mix of collations错误。解决办法是拼接前用 CONVERT 统一字符集,比如CONVERT(col USING utf8mb4)。这类问题在从旧库迁移、或者多库 JOIN 时更容易出现。

内存方面,超大的group_concat_max_len设置会让每个涉及GROUP_CONCAT的查询占用的内存随之上升。不是查询里写了GROUP_CONCAT就会立刻爆内存,但高并发场景下几十个线程同时拼接大字符串,数据库内存会明显上涨。因此在调大配置时一定要考虑连接数和并发度,别只盯着单一查询。

4.4 常见问题速查表

现象可能原因解决办法
拼接结果被截断group_concat_max_len 太小调大会话/全局/配置文件参数
拼接后顺序混乱外层 ORDER BY 与内部排序冲突在 GROUP_CONCAT 内部写 ORDER BY
结果里出现重复值未加 DISTINCT 或去重粒度错明确 DISTINCT 的表达式范围
结果显示 NULL所有参与拼接的值都是 NULLIFNULL/COALESCE 处理或过滤空值
报错 ONLY_FULL_GROUP_BYselect 非聚合列未出现在 GROUP BY补全 GROUP BY 或使用 ANY_VALUE
报错 Illegal mix of collations多表字段字符集/排序规则不一致用 CONVERT 统一字符集
拼接结果超大,响应慢输出字节大、内存排序消耗高控制字段宽度、索引优化、考虑 JSON_ARRAYAGG

这张表是我自己排查问题时常用的框架,每次遇到类似报错,先定位现象再套原因,效率会高很多。

5. 面试题视角与扩展思考

5.1 面试官问 GROUP_CONCAT 想考什么

MySQL 面试题里,GROUP_CONCAT属于容易被轻视的知识点。面试官如果让你写“查出每个部门员工姓名拼接在一起”的 SQL,表面上考的是语法,实际想确认你对聚合函数的边界条件有没有清醒认识。我见过不少候选人能写出基础 SQL,但一问出下面这几个问题就露馅:

  • 拼接结果默认最长多少?截断会不会报错?
  • DISTINCT 去重的单位是什么?
  • 内部 ORDER BY 能写成什么形式?
  • 能不能嵌套子查询?
  • 多个一对多 JOIN 时拼接为什么会出现重复?

这些问题背后其实考察的是你踩过多少坑。我自己在面试时也喜欢用GROUP_CONCAT当引子,从基础语法往深度场景带,判断候选人数据库动手能力到底到什么级别。很多候选人背了一堆高并发、索引优化概念,结果连“CLOB拼接限制”这种实战问题都答不清,说明平时写SQL还是太依赖复制粘贴。

5.2 替代方案的正确选型

前文提到了JSON_ARRAYAGG和窗口函数,这里集中对比一下:

方案输出排序支持长度限制适合场景
GROUP_CONCAT分隔符字符串支持内部 ORDER BY受 group_concat_max_len 限制简单展示、CSV/日志输出
JSON_ARRAYAGGJSON 数组需搭配窗口函数/子查询无传统长度限制前端直接解析、下游结构化消费
子查询 + GROUP_CONCAT分隔符字符串分组内先排序再聚合同上复杂条件限制后的拼接
窗口函数 + 条件聚合多列/多行窗口内排序无需要保留多列统计值时

选型核心看数据消费方。如果只是给人看的,GROUP_CONCAT最直接;如果给程序消费,JSON 更稳;如果数据量超大且要对拼接结果再做统计,建议直接在聚合前用窗口函数把数据量压下来,不要把所有明细都拼出来再截断。

我在实际项目中还遇到过用GROUP_CONCAT给一批 ID 拼成一个字符串,再配合FIND_IN_SET做条件判断的写法。这招在数据量不大时非常方便,但数据量一大性能就很差,因为它用不到索引。更推荐的做法是把字符串拆开 JOIN 回原表查询,或用临时表。你要记住一条原则:GROUP_CONCAT的目的是“展示”,不是“作为查询条件去关联”,一旦把它当中间表用,坑就来了。

个人实际使用下来,GROUP_CONCAT最舒服的场景是监控告警、日报导出和目标字段快速透视。我最后还会分享一个小技巧:如果你需要把拼接后的结果再次拆分并按条件统计,MySQL 8.0 可以用JSON_TABLE()配合拆分 JSON 数组,但如果你用的是 5.7,就老老实实把原始明细表梳理清楚,尽量不要依赖字符串的二次切割。数据结构的规范性永远比函数技巧更重要,这一点在GROUP_CONCAT上体现得特别明显。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/3 9:28:56

CAXA二次开发为何必须用ObjectCRX而非.NET或COM

1. 为什么CAXA二次开发必须用ObjectCRX&#xff0c;而不是.NET API或COM接口&#xff1f;在工业软件生态里&#xff0c;CAXA作为国产CAD/CAM平台的代表&#xff0c;其二次开发体系长期被误读为“简单封装的COM组件”或“类AutoCAD的.NET插件”。但实际深入到制造企业现场会发现…

作者头像 李华
网站建设 2026/10/3 9:26:10

鸿蒙Flutter适配:纯Dart RSS解析库webfeed_plus实战指南

1. 为什么是 webfeed_plus&#xff1a;被忽略的“纯 Dart”属性才是鸿蒙适配关键先把结论放在前面&#xff1a;我接手鸿蒙化适配时&#xff0c;项目里一共用了二十多个 Flutter 三方库&#xff0c;最后编译环节真正零改动跑通的凤毛麟角&#xff0c;webfeed_plus 是其中表现最省…

作者头像 李华
网站建设 2026/10/3 9:24:34

RUN-LSSVM实战:龙格库塔优化器自动调参的分类预测全流程

说实话&#xff0c;我入行做机器学习建模这些年&#xff0c;各种“XX优化器LSSVM”的组合见了不少&#xff0c;大多数是套个壳骗引用量的。但最近在优化一个分类模型时偶然试了试RUN-LSSVM&#xff0c;把数值计算里的龙格库塔法和最小二乘支持向量机绑在一起&#xff0c;这个组…

作者头像 李华
网站建设 2026/10/3 9:23:55

动态多条件求平均:用AVERAGEIFS构建薪酬分析控制台

1. 从工资表到薪酬分析的最后一公里先聊个比较实际的问题。做薪酬分析的人&#xff0c;应该都经历过类似场景&#xff1a;老板丢过来一张上千行的工资明细表&#xff0c;说"看一下今年各事业部技术岗的平均绩效奖金是多少&#xff1f;跟去年比涨了还是降了&#xff1f;&qu…

作者头像 李华
网站建设 2026/10/3 9:23:53

解压即用的本地OCR服务:支持中英日韩的离线文字识别方案

简介&#xff1a;这是一套可本地部署的免费OCR文字识别服务&#xff0c;面向需要批量提取图片文字、又不想承担在线OCR调用费用的开发者与办公用户。它模仿在线OCR的调用方式&#xff0c;运行后开启Web服务&#xff0c;通过POST请求即可完成识别&#xff0c;支持中文、英文、日…

作者头像 李华