身份证号第7至14位表示出生日期,格式为yyyymmdd的8位数字,需用str_to_date转换为date类型方可安全参与日期运算。

身份证号第7到第14位就是出生日期,但要注意格式和校验
中国大陆18位身份证号的第7–14位是8位数字,对应YYYYMMDD格式的出生日期(如19900315)。直接用SUBSTR()提取即可,但别忘了:这串数字不一定是合法日期——比如19901399能被SUBSTR取出来,但它根本不是真实日期。
-
SUBSTR(id_card, 7, 8)是标准写法;MySQL中SUBSTR()和SUBSTRING()完全等价 - 如果字段是
VARCHAR(18)且确保每行都合规,可直接用;若存在短于18位、空值或乱码数据,会返回NULL或截断结果 - 想转成
DATE类型参与计算?得套一层STR_TO_DATE():STR_TO_DATE(SUBSTR(id_card, 7, 8), '%Y%m%d') - 注意时区无关——
SUBSTR纯字符串操作,不涉及任何日期解析逻辑
为什么用SUBSTR而不是LEFT/RIGHT或正则?
因为定位明确:出生日期固定在第7–14位。用LEFT(RIGHT(id_card, 12), 8)多绕两步,性能略差;而REGEXP_SUBSTR()(MySQL 8.0+)虽然灵活,但对固定位置提取属于杀鸡用牛刀,且正则引擎开销更大。
-
SUBSTR(id_card, 7, 8)执行快、语义清、兼容所有MySQL版本(包括5.7) - 如果要兼容15位老身份证(已罕见),需先判断长度:
CASE WHEN LENGTH(id_card) = 15 THEN SUBSTR(id_card, 7, 6) ELSE SUBSTR(id_card, 7, 8) END,再补19前缀 - MySQL 8.0+ 可用
REGEXP_SUBSTR(id_card, '[0-9]{8}', 1, 1),但无法保证一定是第7位起——万一前6位碰巧有8个连续数字就错了
常见错误:取出来是字符串,但当成日期用了
直接SUBSTR(id_card, 7, 8)返回的是CHAR(8)或VARCHAR,不是DATE。如果后续做BETWEEN '19900101' AND '20001231'还能凑合,但一旦用YEAR()、DATE_ADD()或索引优化,就会出问题或失效。
- 错误写法:
WHERE YEAR(SUBSTR(id_card, 7, 8)) = 1990→ 报错或隐式转换失败 - 正确写法:
WHERE YEAR(STR_TO_DATE(SUBSTR(id_card, 7, 8), '%Y%m%d')) = 1990 - 更高效做法:在应用层或ETL阶段把出生日期单独存为
DATE字段并建索引,避免每次查询都调用函数 - 注意
STR_TO_DATE()遇到非法日期(如19900230)会返回NULL,需配合IS NOT NULL过滤
实际查询示例与性能提醒
假设表叫user_info,身份证字段为id_card:
SELECT
id_card,
SUBSTR(id_card, 7, 8) AS birth_str,
STR_TO_DATE(SUBSTR(id_card, 7, 8), '%Y%m%d') AS birth_date
FROM user_info
WHERE id_card IS NOT NULL
AND LENGTH(id_card) = 18
AND SUBSTR(id_card, 7, 8) REGEXP '^[0-9]{8}$';
- 加
LENGTH(id_card) = 18和正则校验,能提前过滤掉脏数据,避免STR_TO_DATE大量返回NULL - 这个查询无法走
id_card字段的索引——因为用了函数。真要高频按出生日期查,得建生成列:ALTER TABLE user_info ADD COLUMN birth_date DATE AS (STR_TO_DATE(SUBSTR(id_card, 7, 8), '%Y%m%d')) STORED,再给该列加索引 - 15位身份证现在极少见,但如果你的数据源老旧,漏掉它可能导致一批用户出生日期全错成
NULL











