mysql无内置url编码函数,需用存储函数按rfc 3986规范实现:强制latin1转换后逐字节处理,保留大小写,utf-8多字节字符正确转%xx格式。

MySQL 里没有内置 URL 编码函数,得自己写
MySQL 原生不提供 urlencode 或类似标准库函数,CONVERT、HEX、REPLACE 拼凑出来的结果也不符合 RFC 3986 规范(比如空格应为 %20 而非 +,字母大小写要保留,非 ASCII 字符需 UTF-8 编码后再 hex)。直接用 REPLACE 手动替换几十个字符既不可靠又难维护。
用存储函数实现符合规范的 URL 编码
核心思路是:逐字节扫描字符串,对每个字节判断是否属于 unreserved 字符(A-Z a-z 0-9 - _ . ~),不是就转成 UTF-8 编码 + 大写十六进制格式(%XX)。注意 MySQL 的 SUBSTRING 是按字符而非字节取,而 URL 编码必须按 UTF-8 字节操作,所以要用 CONVERT(... USING latin1) 强制转成单字节上下文再处理。
示例函数定义:
DELIMITER $$ CREATE FUNCTION urlencode(str TEXT) RETURNS TEXT READS SQL DATA DETERMINISTIC BEGIN DECLARE i INT DEFAULT 1; DECLARE len INT DEFAULT 0; DECLARE c CHAR(1); DECLARE result TEXT DEFAULT ''; DECLARE ord_val INT DEFAULT 0; <p>IF str IS NULL THEN RETURN NULL; END IF;</p><p>SET len = CHAR_LENGTH(CONVERT(str USING latin1)); WHILE i </p><pre class="brush:php;toolbar:false;">-- unreserved: A-Z a-z 0-9 - _ . ~ IF (ord_val BETWEEN 65 AND 90) OR -- A-Z (ord_val BETWEEN 97 AND 122) OR -- a-z (ord_val BETWEEN 48 AND 57) OR -- 0-9 ord_val IN (45, 95, 46, 126) THEN -- - _ . ~ SET result = CONCAT(result, c); ELSE SET result = CONCAT(result, '%', LPAD(UPPER(HEX(c)), 2, '0')); END IF; SET i = i + 1;
END WHILE;
RETURN result; END$$ DELIMITER ;
使用方式:SELECT urlencode('hello world+测试'); → hello%20world%2B%E6%B5%8B%E8%AF%95
- 必须用
CONVERT(str USING latin1),否则SUBSTRING在多字节字符上会截断乱码 -
HEX(c)对单字节有效;对中文等,CONVERT(... USING latin1)实际把 UTF-8 字节流当 Latin1 解释,刚好让每个 UTF-8 字节单独被HEX处理 - 不能用
CHAR_LENGTH(str)直接算长度——那是字符数,不是 UTF-8 字节数 - 性能较差,不适合在大表
SELECT中批量调用;仅建议用于少量数据预处理或日志生成
常见错误:用 REPLACE 硬编码几个符号就叫“URL 编码”
这种写法看似简单,但漏洞明显:SELECT REPLACE(REPLACE('a b+c', ' ', '%20'), '+', '%2B');
- 漏掉几乎所有需要编码的字符(如
?、&、=、€、中文等) - 重复编码风险:如果输入已含
%20,可能变成%2520 - 大小写混乱:
%20和%20等价,但规范推荐大写,有些服务端校验严格 - 无法处理 Unicode:UTF-8 多字节字符(如
测是E6 B5 8B)必须拆成三个%编码段,硬REPLACE完全无能为力
替代方案:优先在应用层做 URL 编码
真正在生产环境,几乎从不在 MySQL 里做 URL 编码。原因很实际:
- PHP/Python/JS 等语言都有成熟、标准、高效、可测试的
urlencode实现 - 数据库只存原始值,编码逻辑统一收口,便于调试和变更(比如某天要改成
encodeURIComponent) - 避免函数跨版本兼容问题(MySQL 5.7 和 8.0 对
CONVERT行为略有差异) - 如果必须存在数据库侧生成(如生成带参跳转链接的视图),至少把函数加上注释说明:“此函数按 RFC 3986 对 UTF-8 字节流编码,不支持 surrogate pair”
真正容易被忽略的是:你写的这个 urlencode 函数,在遇到 NULL、空字符串、超长字符串(> 1MB)、含控制字符(U+0000)时的行为——这些边界情况,函数体里要显式判断,而不是依赖 MySQL 默认截断或报错。











