MySQL无内置函数直接提取所有连续数字,8.0+的REGEXP_SUBSTR仅返回首个匹配项;需用自定义函数配合WHILE循环逐段提取,每次调用返回下一个数字,外部需维护起始位置状态。

MySQL 中没有内置函数能直接提取字符串里的连续数字
MySQL 原生不提供类似正则提取捕获组(如 REGEXP_SUBSTR)的功能,5.7 及更早版本连 REGEXP_SUBSTR 都不存在;8.0+ 虽支持,但仅返回**第一个匹配项**,无法循环提取所有连续数字段。所以想“把 'abc123def4567gh89' 拆成 123、4567、89”,必须靠自定义函数 + 循环逻辑。
用存储函数 + WHILE 实现逐段提取数字
核心思路是:用 REGEXP 定位数字起始位置,用 SUBSTRING 截取,再更新剩余字符串继续找。注意 MySQL 函数不能返回结果集,只能单次返回一个值,所以这个函数设计为「每次调用返回下一个连续数字」,需配合外部循环(比如在存储过程里反复调用)。
实操建议:
- 函数入参为原始字符串和当前搜索起始位置(初始传 1)
- 用
LOCATE或REGEXP判断是否存在数字:推荐str REGEXP '[0-9]',但注意它不返回位置 —— 所以实际要用REGEXP_REPLACE配合占位符间接定位,或改用LOCATE配合循环扫描(更稳) - 真正可靠的做法是:从
pos开始逐字符检查SUBSTRING(str, pos, 1)是否匹配'[0-9]',找到第一个数字后,向后扩展直到非数字为止 - 函数必须声明
DETERMINISTIC,否则创建失败
示例关键片段(简化版):
DELIMITER $$ CREATE FUNCTION extract_next_number(str TEXT, start_pos INT) RETURNS VARCHAR(50) READS SQL DATA DETERMINISTIC BEGIN DECLARE i INT DEFAULT start_pos; DECLARE len INT DEFAULT CHAR_LENGTH(str); DECLARE num_start INT DEFAULT 0; DECLARE num_end INT DEFAULT 0; <p>-- 找数字起点 WHILE i </p><p>IF num_start = 0 THEN RETURN NULL; END IF;</p><div class="aritcle_card flexRow artxards"> <div class="artcardd flexRow"> <a class="aritcle_card_img" rel="nofollow" href="/xiazai/skill2334" title="MySQL"><img src="https://img.php.cn/upload/skill/000/000/081/178900927846657.jpg" alt="MySQL" onerror="this.onerror='';this.src='/static/lhimages/moren/morentu.png'" ></a> <div class="aritcle_card_info flexColumn"> <a rel="nofollow" href="/xiazai/skill2334" title="MySQL" class="overflowclass">MySQL</a> <p class="overflowclass">编写正确的MySQL查询,避免字符集、索引和锁方面的常见陷阱。</p> </div> <a rel="nofollow" href="/xiazai/skill2334" title="MySQL" class="aritcle_card_btn flexRow flexcenter"><b></b><span>下载</span> </a> </div> </div><p>-- 找数字终点 SET i = num_start; WHILE i </p><p>RETURN SUBSTRING(str, num_start, num_end - num_start + 1); END$$ DELIMITER ; </p>
调用时必须自己维护状态,不能“一键全出”
这个 extract_next_number 函数本身不记录上次提取到哪,每次调用都是独立的。所以你不能写 SELECT extract_next_number('a1b22c333', 1) 就拿到全部 —— 它只返回 '1'。要拿到全部,得在外面套一层逻辑:
- 在存储过程中用
WHILE循环,每次传入新起始位置(比如上一次结束位置 + 1) - 起始位置不能硬写 1,而要动态计算:上一轮返回了
'123'且起始于 4,长度 3 → 下次从 7 开始 - 函数返回
NULL表示无更多数字,作为循环退出条件 - 别忘了处理空格、负号、小数点等边界情况 —— 当前函数只认纯阿拉伯数字连续段
8.0+ 用户可考虑 JSON + 正则临时方案(不推荐生产)
有人用 REGEXP_REPLACE 把非数字全替换成逗号,再用 JSON_TABLE 拆分,例如:
SELECT TRIM(val) AS num
FROM JSON_TABLE(
CONCAT('["', REGEXP_REPLACE('abc123def4567', '[^0-9]+', '","'), '"]'),
'$[*]' COLUMNS (val TEXT PATH '$')
) AS jt
WHERE val != '';
但这只是“看起来像提取”,实际依赖字符串拼接和 JSON 解析,对含引号、反斜杠的原始数据会崩,且性能差、不可读。真正需要稳定提取,请坚持用带状态管理的自定义函数。
最易被忽略的一点:MySQL 函数内不能执行 SELECT ... INTO 写变量(除非目标是用户变量),也不能调用会修改数据的语句。所有逻辑必须靠纯计算和字符串操作完成 —— 这限制了容错空间,比如超长数字可能被截断为科学计数法,得提前用 CAST(... AS CHAR) 控制格式。










