身份证第7–14位为YYYYMMDD格式出生日期字符串,需用STR_TO_DATE()转为DATE类型;必须前置校验CHAR_LENGTH(id_card)=18且非NULL,否则MID可能返回无效片段导致转换为NULL;建议添加STORED生成列并建索引提升查询性能。

身份证号第7–14位就是出生日期,但直接截取是字符串,不是日期类型
中国大陆18位身份证号中,第7到第14位(共8位)严格对应YYYYMMDD格式的出生日期,比如19900520。用MID()能拿到这8位,但结果仍是字符串——没法参与日期计算、排序或用BETWEEN查范围。必须转成真正的DATE类型,STR_TO_DATE()就是干这个的。
常见错误是直接写STR_TO_DATE(MID(id_card, 7, 8), '%Y%m%d')却没检查空值或非法长度,导致整行返回NULL;更隐蔽的问题是,有些身份证号末位是字母X,但前17位数字结构不变,不影响第7–14位提取——只要字段值本身合法,就不用额外处理X。
必须先过滤掉非18位或NULL的身份证号,否则MID会静默出错
MID()对长度不足的字符串不会报错,而是返回空字符串或截断结果,比如对15位旧身份证或空值调用MID(id_card, 7, 8),可能得到'0520'这种无效片段,再喂给STR_TO_DATE()就变成NULL,且无提示。所以实际使用时一定要前置校验:
- 用
CHAR_LENGTH(id_card) = 18排除15位旧证和补位错误数据 - 加
id_card IS NOT NULL避免MID(NULL, 7, 8)返回NULL后继续传递 - 可选:用
id_card REGEXP '^[0-9]{17}[0-9Xx]$'粗筛格式(注意MySQL 8.0+才支持Xx写法,5.7需拆成两个OR)
STR_TO_DATE转换失败时返回NULL,别指望它报错或跳过
STR_TO_DATE('19901301', '%Y%m%d')不会报错,也不会警告,而是安静返回NULL——因为13月不存在。同理,'19900230'(2月30日)、'abcd1234'都会得NULL。这意味着你查“1990年出生的人”时,如果原始身份证号有录入错误,这部分人就直接从结果里消失了,且难以察觉。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
建议做法:
- 在WHERE条件里加
STR_TO_DATE(MID(id_card, 7, 8), '%Y%m%d') IS NOT NULL显式过滤掉转换失败的行 - 上线前用
SELECT id_card, MID(id_card, 7, 8) FROM user WHERE CHAR_LENGTH(id_card)=18 LIMIT 100抽样看第7–14位是否真为合法日期 - 避免在大表上直接
SELECT STR_TO_DATE(...)做排序或分组——函数无法走索引,会全表扫描
生产环境建议加生成列(Generated Column)提升查询性能
每次查都实时算MID + STR_TO_DATE,对高频查询很伤。MySQL 5.7+支持虚拟生成列,可以固化这个逻辑:
ALTER TABLE user ADD COLUMN birth_date DATE AS (STR_TO_DATE(MID(id_card, 7, 8), '%Y%m%d')) STORED;
加上STORED后,值物理存储,还能建索引:
CREATE INDEX idx_birth_date ON user(birth_date);- 后续查
WHERE birth_date BETWEEN '1990-01-01' AND '1999-12-31'就能走索引 - 注意:生成列依赖
id_card,如果该字段被UPDATE,birth_date自动更新,无需手动维护
唯一要盯紧的是:加生成列会锁表(除非用ALGORITHM=INPLACE,且满足MySQL版本与引擎限制),线上操作务必选低峰期并提前测试。

















