ASCII()和UNICODE()均返回字符串首字符的码点整数值;前者适用于单字节字符集,后者适用于Unicode,且UNICODE()更可靠识别非标准字符。
ASCII() 和 UNICODE() 都只作用于字符串的第一个字符,返回其对应的码点整数值。前者专用于单字节字符集(如Latin1),后者用于Unicode(UTF-16或UTF-8环境下的首字符码位)。在MySQL 8.0+、PostgreSQL、SQL Server中行为一致;但SQLite默认不支持UNICODE(),需用unicode()(小写)替代。
ASCII('A') → 65 UNICODE('€') → 8364(U+20AC) ASCII('€') → 可能报错,或返回第一个字节值(如 UTF-8 下为 226),不可靠 关键判断依据:若想识别非标准字符(比如控制符、零宽空格、组合符号、私有区字符),应优先用UNICODE(),并检查返回值是否落在常规可打印范围外。
核心思路是逐字符拆解 + 码点判断。不同数据库实现方式差异大,但通用策略是:
UNICODE(SUBSTRING(col, n, 1))结果与预设白名单对比 以SQL Server为例,可用递归CTE生成位置序列,再逐位提取:
WITH cte AS ( SELECT id, col, 1 AS pos FROM your_table UNION ALL SELECT id, col, pos + 1 FROM cte WHERE pos < LEN(col))SELECT id, col, pos, UNICODE(SUBSTRING(col, pos, 1)) AS cpFROM cteWHERE UNICODE(SUBSTRING(col, pos, 1)) NOT BETWEEN 32 AND 126 AND UNICODE(SUBSTRING(col, pos, 1)) NOT BETWEEN 19968 AND 40869 AND UNICODE(SUBSTRING(col, pos, 1)) NOT IN (9, 10, 13); -- 保留制表、换行、回车
注意:MySQL 8.0+ 可用REGEXP_LIKE(col, '[^[:print:]]')快速初筛,但它不区分Unicode码点,仅按当前collation判断“可打印”,对中文无效。
因为ASCII()不是为多字节字符设计的。在UTF-8存储下,一个中文字符占3个字节,ASCII()只取第一个字节(如'你'的UTF-8是 E4 BD A0,ASCII('你') 返回 228),这个值既不是Unicode码点,也不具备语义一致性。
varchar且使用Latin1编码,存入中文会转成问号或乱码,此时ASCII()返回63(?的码点) ASCII()对非ASCII字符直接报错:ERROR: invalid byte sequence for encoding "UTF8" 所以只要字段可能含中文、Emoji、特殊符号,一律弃用ASCII(),改用UNICODE()(或PostgreSQL的get_byte()+编码转换,但复杂度陡增)。
U+200B零宽空格、U+FEFFBOM)肉眼不可见,但LEN()或CHAR_LENGTH()会计数,UNICODE()能准确捕获 U+00E9,也可能是U+0065+U+0301两个码点),后者会导致长度翻倍且校验逻辑失效 utf8mb4并设置正确collation U+3000)、NO-BREAK SPACE(U+00A0)、EN QUAD(U+2000)都不同于ASCII空格(32),单纯TRIM()无效 真正要落地识别非标准字符,得先明确“标准”的定义——是业务允许的字符集,还是协议规范(如JSON要求仅允许某些Unicode块),而不是依赖某个函数能否“跑通”。