Oracle和PostgreSQL支持在行级触发器中用正则校验身份证号合法性,MySQL不支持;需结合REGEXP_LIKE(Oracle)或~(PG)初筛格式,并调用函数计算加权校验码,不通过则抛异常中断操作。

行级触发器中用正则校验身份证号是否合法
Oracle 和 PostgreSQL 支持在行级触发器(BEFORE INSERT OR UPDATE)里做身份证格式校验,MySQL 不支持触发器内调用正则函数进行写时校验(NEW 值不可被正则函数在 BEFORE 触发器中可靠拦截),所以实际能稳定落地的只有 Oracle 和 PG。
核心思路是:在 BEFORE INSERT OR UPDATE 触发器中对 NEW.id_card 字段做正则匹配 + 校验码验证,不通过就抛异常中断操作。
- 18 位身份证需满足:前 17 位为数字,第 18 位为数字或 X/x;且加权校验码必须匹配
- 15 位老身份证(已停用但业务中仍可能遇到)仅含数字,无校验码,建议按业务策略决定是否允许
- Oracle 用
REGEXP_LIKE初筛,再用 PL/SQL 函数算校验码;PG 用~匹配 +substring()提取后计算
Oracle 行级触发器校验示例(含校验码逻辑)
Oracle 触发器里不能直接在 SQL 层完成完整校验码计算,需封装成函数再调用。先建校验函数:
CREATE OR REPLACE FUNCTION is_valid_idcard(p_id IN VARCHAR2) RETURN NUMBER IS
v_sum NUMBER := 0;
v_weight CONSTANT VARCHAR2(17) := '79105842163795842';
v_checkcode CONSTANT VARCHAR2(11) := '10X98765432';
v_i NUMBER;
v_c CHAR(1);
BEGIN
IF p_id IS NULL OR LENGTH(p_id) NOT IN (15, 18) THEN
RETURN 0;
END IF;
<p>IF LENGTH(p_id) = 18 THEN
-- 检查前17位是否全数字,第18位是否为数字/X/x
IF NOT REGEXP_LIKE(p_id, '^\d{17}[\dXx]$') THEN
RETURN 0;
END IF;
-- 计算加权和
FOR i IN 1..17 LOOP
v_c := SUBSTR(p_id, i, 1);
v_i := TO_NUMBER(v_c) * TO_NUMBER(SUBSTR(v_weight, i, 1));
v_sum := v_sum + v_i;
END LOOP;
IF UPPER(SUBSTR(p_id, 18, 1)) != SUBSTR(v_checkcode, MOD(v_sum, 11) + 1, 1) THEN
RETURN 0;
END IF;
ELSIF LENGTH(p_id) = 15 THEN
IF NOT REGEXP_LIKE(p_id, '^\d{15}$') THEN
RETURN 0;
END IF;
ELSE
RETURN 0;
END IF;
RETURN 1;
END;再建触发器:
CREATE OR REPLACE TRIGGER tr_check_idcard
BEFORE INSERT OR UPDATE ON users
FOR EACH ROW
BEGIN
IF is_valid_idcard(:NEW.id_card) = 0 THEN
RAISE_APPLICATION_ERROR(-20001, '身份证号格式不合法');
END IF;
END;PostgreSQL 中触发器写法差异点
PG 的触发器函数需返回 TRIGGER 类型,且正则语法用 ~,大小写不敏感需加 ~*;校验码计算可用 substring() + ascii() 实现。关键注意:
- PG 触发器函数必须显式
RETURN NEW,否则新行会被丢弃 -
NEW.id_card是 text 类型,参与数值运算前需::int转换,空值要COALESCE处理 - PG 不支持在触发器中直接抛出带消息的异常(除非用
RAISE EXCEPTION),错误信息会原样传给客户端 - 避免在触发器里调用外部函数或查询表,否则影响并发性能
简化版校验逻辑(仅格式初筛,不含加权校验):
CREATE OR REPLACE FUNCTION check_idcard_func()
RETURNS TRIGGER AS $$
BEGIN
IF NEW.id_card !~ '^\d{17}[\dXx]$' AND NEW.id_card !~ '^\d{15}$' THEN
RAISE EXCEPTION '身份证号格式不合法:必须为15位纯数字或18位(前17位数字,末位数字/X/x)';
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;为什么不在应用层校验而要用触发器
触发器校验是兜底手段,不是首选方案。它真正起作用的场景很窄:
- 存在多套应用直连数据库(如旧 Java 系统 + 新 Python 脚本 + DBA 手动 INSERT),无法统一约束入口
- 需要防止通过
INSERT /*+ APPEND */或物化视图刷新等绕过 ORM 的写入 - 审计要求“数据入库即合规”,且不允许事后清洗
但代价明显:触发器逻辑难调试、无法单元测试、升级迁移时容易遗漏、PG 中还可能干扰并行 COPY。更推荐的做法是——把校验逻辑下沉到数据库函数,再由应用层显式调用 SELECT is_valid_idcard(?) 预检,失败则拒绝提交。触发器只作为最后一道保险,且应禁用在高吞吐写入表上。
校验码算法本身有固定实现,但不同数据库对字符串索引、大小写转换、模运算溢出的处理略有差异,上线前务必用真实身份证号样本(含末位 X、末位 0、特殊地区编码)全覆盖验证。

















