仅用REGEXP无法100%验证身份证真伪,需结合STR_TO_DATE校验日期有效性及手算第18位校验码:先用正则筛格式(如^[1-9]d{5}(19|20)d{2}(0[1-9]|1[0-2])(0[1-9]|[12]d|3[01])d{3}[dX]$),再提取第7–14位转日期判NULL或超限,最后按权重加权模11比对校验码。

触发器里用 REGEXP 校验身份证基本格式是否合法
只靠正则无法 100% 验证身份证真伪,但能快速筛掉明显非法的字符串,比如长度不对、含非法字符、末位不是数字或 X。MySQL 触发器中必须用 REGEXP(不是 RLIKE,二者等价但写法统一更稳妥),且注意大小写:X 必须允许大写,小写 x 在严格校验场景下应拒绝(国标要求为大写)。
常见错误现象:NEW.id_card REGEXP '^[0-9]{17}[0-9X]$' 看似合理,但漏了首位不能为 0 的约束(地址码第一位是 1–9),也未限制出生年份范围(如 0000 年)、月份日期逻辑(如 0230)。实际生产中建议至少收紧到:^[1-9]\d{5}(19|20)\d{2}(0[1-9]|1[0-2])(0[1-9]|[12]\d|3[01])\d{3}[\dX]$。
- 必须用
BEFORE INSERT或BEFORE UPDATE,否则数据已入库再报错无意义 -
REGEXP返回 1/0,不能直接用于IF判断布尔值,要写成IF NEW.id_card REGEXP '...' = 0 THEN - MySQL 8.0+ 支持
\d,但 5.7 及更早版本建议用[0-9]兼容性更好
触发器中提取出生日期并用 STR_TO_DATE 校验有效性
光匹配格式不够,比如 11010120991399123X 能过正则,但 2099 年还没到、13 月不存在。触发器里可调用 STR_TO_DATE(SUBSTRING(NEW.id_card, 7, 8), '%Y%m%d') 提取年月日并转为日期类型,再判断是否为 NULL 或大于当前时间。
容易踩的坑:SUBSTRING 起始位置是 1(非 0),第 7–14 位才是出生日期;若传入非法日期(如 20231301),STR_TO_DATE 直接返回 NULL,不报错——所以必须显式判断 IS NULL,不能只看是否等于某个值。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
- 务必加
AND LENGTH(NEW.id_card) = 18前置判断,避免对 15 位旧号或空字符串调用SUBSTRING -
STR_TO_DATE在 SQL mode 含NO_ZERO_DATE时对 0000 年等非法值更敏感,建议确认当前 mode - 性能影响:每次 INSERT/UPDATE 都执行字符串切片 + 日期解析,高频写入表需权衡
触发器内实现校验码(第 18 位)计算与比对
真正区分真假身份证的关键是最后一位校验码。它由前 17 位按固定权重(7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2)加权求和后对 11 取模,再映射到 '1','0','X','9','8','7','6','5','4','3','2'。触发器里必须手写这 17 步乘加,不能偷懒用循环(MySQL 存储过程不支持动态数组索引)。
典型错误写法:MOD(SUBSTRING(NEW.id_card,1,17),11) —— 这是把整个 17 位当一个整数取模,完全错误。正确做法是逐位转整型再乘权重。
- 每一步都要用
CAST(SUBSTRING(NEW.id_card, n, 1) AS UNSIGNED)转数字,否则遇到X会静默变 0 - 第 18 位若为小写
x,需先UPPER()统一,再与映射表比对 - 映射结果必须严格对应:余数 0 →
'1',余数 1 →'0',余数 2 →'X'……余数 10 →'2'
用 SIGNAL SQLSTATE '45000' 中断非法插入并返回自定义错误
触发器里不能用 RETURN 或 SELECT 报错,唯一可靠方式是 SIGNAL。错误码选 '45000'(用户自定义异常),MESSAGE_TEXT 最好包含具体失败原因,比如 'Invalid checksum digit',方便应用层捕获处理。
注意:SIGNAL 会立即终止当前语句执行,事务回滚,但不会自动打印堆栈。如果触发器里有多个校验点(格式、日期、校验码),建议分段 IF + SIGNAL,而不是全塞进一个条件里,否则出错时无法定位是哪步挂了。
- 不要在
SIGNAL前写SET @msg = ...再引用变量,MySQL 不允许在SIGNAL中使用用户变量 - 错误信息长度限制 128 字符,超长会被截断,关键信息放前面
- 测试时用
INSERT INTO ... VALUES (...)触发,别用LOAD DATA INFILE,后者绕过触发器

















