SUBSTRING提取身份证出生日期需按数据库选择函数及起始位:MySQL/PG/SQL Server/SQlite用SUBSTRING(id_card,7,8),Oracle用SUBSTR;15位证需补“19”并校验长度;转换日期须防脏数据报错;WHERE中直接截取无法走索引,应建生成列或ETL预处理。

SUBSTRING 提取身份证出生日期的通用写法
身份证号第7到第14位是8位数字格式的出生日期(YYYYMMDD),直接用 SUBSTRING 截取即可,但要注意不同数据库对起始位置的定义差异。
- MySQL、PostgreSQL、SQL Server(从1开始计数):
SUBSTRING(id_card, 7, 8) - Oracle(也从1开始):
SUBSTR(id_card, 7, 8)(注意函数名是SUBSTR) - SQLite(同样从1开始):
SUBSTR(id_card, 7, 8)
比如身份证 '11010119900307235X',执行 SUBSTRING(id_card, 7, 8) 得到 '19900307'。
把截出的8位数字转成标准日期类型
单纯截取只是字符串,要参与日期计算或格式化显示,必须转换。各库转换方式不一致,且容易因格式错误报错:
- MySQL:
STR_TO_DATE(SUBSTRING(id_card, 7, 8), '%Y%m%d'),失败返回NULL - PostgreSQL:
TO_DATE(SUBSTRING(id_card, 7, 8), 'YYYYMMDD'),非法值直接报错,建议加WHERE id_card ~ '^[0-9]{18}$'预过滤 - SQL Server:
CONVERT(DATE, SUBSTRING(id_card, 7, 8), 112)(112 表示 yyyymmdd 格式) - Oracle:
TO_DATE(SUBSTR(id_card, 7, 8), 'YYYYMMDD'),需确保字段非空且全为数字
没做校验就强转,遇到 '110101XXXXXX235X' 这类脏数据会中断查询。
处理15位老身份证的兼容逻辑
15位身份证没有年份前两位(只有YYMMDD),第7–12位是出生日期。直接统一用位置7截8位会越界或错读。稳妥做法是先判断长度:
- MySQL 示例:
CASE WHEN LENGTH(id_card) = 18 THEN SUBSTRING(id_card, 7, 8) WHEN LENGTH(id_card) = 15 THEN CONCAT('19', SUBSTRING(id_card, 7, 6)) ELSE NULL END - 注意:15位身份证默认是1901–1999年出生,不能简单补'20';若业务明确无1900年前出生者,可统一补'19',但需和业务方确认
WHERE 条件中用 SUBSTRING 做范围筛选的性能陷阱
在 WHERE SUBSTRING(id_card, 7, 8) >= '19950101' 这类条件里,SUBSTRING 是非SARGable操作,无法走索引,全表扫描不可避免。
- 优化方向:建生成列(MySQL 5.7+/PostgreSQL)或计算列(SQL Server),再在该列上建索引
ALTER TABLE users ADD COLUMN birth_date DATE GENERATED ALWAYS AS (STR_TO_DATE(SUBSTRING(id_card, 7, 8), '%Y%m%d')) STORED;
- 或者前置ETL清洗,把出生日期单独存为规范字段——线上查询别依赖实时截取
真实生产环境里,身份证解析逻辑往往藏在视图或UDF里,一旦被多层嵌套调用,出问题时很难定位到底是截取错、转换错,还是原始数据本身就不合规。










