SQL怎样从身份证号中提取出生日期_利用SUBSTRING与CAST转换
身份证号第7至14位表示出生日期,格式为YYYYMMDD,需确保字段为字符串类型后截取并显式转换为DATE类型,否则易因类型错误、脏数据或索引失效导致查询失败。

免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
身份证号第7到第14位就是出生日期,但得先确认格式
都知道中国大陆18位身份证号的第7到第14位是YYYYMMDD格式,比如19950823。但这里有个关键前提:你得确保数据库里存的这个字段是字符串类型。如果它被存成了数值型(比如BIGINT),那么后续的SUBSTRING操作要么直接报错,要么截取出莫名其妙的结果。常见的坑包括:SUBSTRING返回空值、截取位置发生偏移,或者后续的CAST转换直接提示“invalid input syntax”。
动手之前,建议先做这几步检查:
- 用
SELECT LENGTH(id_card) FROM table_name LIMIT 5快速验证一下,看看是不是全是18位。如果有15位的老号码,需要单独处理(本文聚焦18位新号)。 - 如果发现
id_card字段是数值类型,必须先把它转成字符串。不同数据库语法略有不同:CAST(id_card AS TEXT)或者id_card::TEXT(PostgreSQL),在SQL Server里则是CONVERT(VARCHAR, id_card)。 - MySQL里可以直接用
CAST(id_card AS CHAR)。更稳妥一点的写法是加上LPAD(id_card, 18, '0')来防止前置零丢失——不过话说回来,18位身份证的首位不可能是0,这一步通常可以省略。
SUBSTRING提取后必须显式CAST为DATE类型
用SUBSTRING(id_card, 7, 8)拿到的,本质上还是一个字符串'19950823'。它可不是日期类型,没法直接参与日期计算、排序,或者跟其他DATE字段进行比较。如果直接写WHERE birth_date > '1990-01-01',数据库会尝试隐式转换,而不同数据库的“脾气”可不一样:MySQL可能容忍,PostgreSQL多半会直接报错,SQL Server则可能按字符串的字典序去比较,结果可想而知。
所以,正确的姿势是显式转换。具体怎么转,还得看数据库:
- PostgreSQL:
CAST(SUBSTRING(id_card, 7, 8) AS DATE)或者简写SUBSTRING(id_card, 7, 8)::DATE。 - SQL Server:
CONVERT(DATE, SUBSTRING(CAST(id_card AS VARCHAR(18)), 7, 8))。这里顺序很重要,得先CAST成字符串再SUBSTRING,因为SUBSTRING函数对纯数字类型可能不买账。 - MySQL:这里有点特殊,得用
STR_TO_DATE(SUBSTRING(id_card, 7, 8), '%Y%m%d')。直接用CAST(... AS DATE)是识别不了'19950823'这种格式的。
遇到NULL或非法值时,SUBSTRING+CAST会中断查询
这才是最让人头疼的地方。只要表里有一条记录的id_card为空、长度不够18位,或者第7到14位里混进了字母之类的非数字字符,整个查询就可能直接报错退出。尤其是在做聚合分析或者创建索引的时候,这种“一颗老鼠屎坏了一锅粥”的情况绝非危言耸听。
怎么办?得加防护判断。比如在PostgreSQL里,可以先用正则清除非数字字符:NULLIF(REGEXP_REPLACE(SUBSTRING(id_card, 7, 8), '\D', '', 'g'), ''),然后再转换。但更治本的建议是,在生产环境里,强烈建议在数据ETL阶段就做好校验和清洗,把异常身份证号单独拎出来处理,而不是等到查询的时候才手忙脚乱地兜底。
如果非要在查询时处理,MySQL里可以写一个相对复杂的判断:IF(LENGTH(id_card)=18 AND id_card REGEXP '^[0-9]{17}[0-9Xx]$', STR_TO_DATE(SUBSTRING(id_card, 7, 8), '%Y%m%d'), NULL)。当然,这会影响性能。
性能影响:SUBSTRING+CAST无法利用索引
另一个容易被忽视的问题是性能。即便你在id_card字段上建了B-Tree索引,像SUBSTRING(id_card, 7, 8)这样的表达式计算是没法走索引的。如果你想查询1990年到1999年出生的人,数据库只能老老实实地做全表扫描,把每条记录都拿出来截取、转换一遍再比较。
对于高频的按出生日期查询的场景,有更优的解决方案:
- 增加冗余字段:最实在的办法,就是在表里直接新增一个
birth_date DATE列。通过触发器或者在应用层写入数据时,就同步解析好并存储进去。一劳永逸。 - 使用函数索引:像PostgreSQL就支持创建函数索引,例如
CREATE INDEX idx_birth ON users ((SUBSTRING(id_card, 7, 8)::DATE))。但要注意,这个索引只对查询条件能完全匹配这个函数表达式的情况有效。 - 避免错误写法:别在WHERE子句里写
CAST(SUBSTRING(id_card, 7, 4) AS INT) BETWEEN 1990 AND 1999。这相当于只截了年份部分来比较,不仅比用完整8位更慢,而且让优化器更加无从下手。
说到底,技术语法本身并不复杂,真正的挑战往往来自于数据本身的不规范。哪怕表中成千上万条记录里,只有那么一两条是15位旧号或者带了空格,就足以让整个批量解析的脚本崩溃。所以,在动手之前,不妨先运行一下这个查询:SELECT id_card FROM t WHERE LENGTH(TRIM(id_card)) NOT IN (15, 18)。看看你的数据到底干不干净,别指望数据库函数能替你搞定所有脏数据。
游乐网为非赢利性网站,所展示的游戏/软件/文章内容均来自于互联网或第三方用户上传分享,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系youleyoucom@outlook.com。
同类文章
mysql怎么把查询结果插入到新表_使用create table select语句
MySQL CREATE TABLE SELECT:轻量建表与数据迁移的利器与陷阱 在数据迁移或快速备份的场景下,CREATE TABLE SELECT 无疑是 MySQL 工具箱里一把轻便的快刀。它能否直接建表并插入数据?答案是肯定的,而且效率颇高。这本质上是一次将“建表”和“插入
Navicat Cloud进阶篇:怎样高效跨组织离职转移项目交接
Na vicat Cloud 项目归属权能直接转给离职同事吗? 答案很明确:不能。Na vicat Cloud 并不支持将项目的“所有权”直接从一个账户过户到另一个账户,尤其是在对方不属于同一个组织(Organization)的情况下。坊间常说的“转移”,其本质是一套组合操作:导出项目文件、重新导入
Redis主从复制全量同步导致主库负载高_配置repl-diskless-sync-delay分批同步
理解 repl-diskless-sync-delay:它并非“分批同步”的开关 先明确一个核心概念:repl-diskless-sync-delay 这个参数,其设计初衷并非为了实现“分批同步”。它的真实作用,是在主库开启了无磁盘同步(即配置了 repl-diskless-sync yes)后,控
Redis怎样避免每次都传输长篇Lua代码
Redis如何高效执行Lua脚本?避免每次传输完整代码的优化方案 核心方案:使用 EVALSHA 替代 EVAL,实现脚本缓存复用 在Redis中频繁通过EVAL命令发送完整的Lua脚本内容,会在高并发场景下产生显著的开销,包括网络传输负载和序列化成本。为了提升性能,Redis提供了EVALSHA命
如何在多服务器之间同步phpMyAdmin偏好设置_用户表集中存储
phpMyAdmin 用户偏好默认存于 MySQL 的 pma__userconfig 表中,需启用高级功能并统一指向中心数据库;跨服务器同步必须共用该表及 controluser,且登录方式不能为 config 模式。 phpMyAdmin 用户偏好存在哪? 很多朋友可能没留意,你每次在 phpM
- 日榜
- 周榜
- 月榜
1
2
3
4
5
6
7
8
9
10
相关攻略
2015-03-10 11:25
2015-03-10 11:05
2021-08-04 13:30
2015-03-10 11:22
2015-03-10 12:39
2022-05-16 18:57
2025-05-23 13:43
2025-05-23 14:01
热门教程
- 游戏攻略
- 安卓教程
- 苹果教程
- 电脑教程
热门话题

