游乐游手机版
首页/数据库/文章详情

SQL怎样从身份证号中提取出生日期_利用SUBSTRING与CAST转换

时间:2026-04-29 18:51
身份证号第7至14位表示出生日期,格式为YYYYMMDD,需确保字段为字符串类型后截取并显式转换为DATE类型,否则易因类型错误、脏数据或索引失效导致查询失败。 身份证号第7到第14位就是出生日期,但得先确认格式 都知道中国大陆18位身份证号的第7到第14位是YYYYMMDD格式,比如1995082

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

SQL怎样从身份证号中提取出生日期_利用SUBSTRING与CAST转换

身份证号第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则可能按字符串的字典序去比较,结果可想而知。

所以,正确的姿势是显式转换。具体怎么转,还得看数据库:

  • PostgreSQLCAST(SUBSTRING(id_card, 7, 8) AS DATE) 或者简写 SUBSTRING(id_card, 7, 8)::DATE
  • SQL ServerCONVERT(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)。看看你的数据到底干不干净,别指望数据库函数能替你搞定所有脏数据。

来源:https://www.php.cn/faq/2320136.html
上一篇Oracle数据库性能基线怎么建?利用AWR快照管理 下一篇SQL Server如何防止视图源码被查看_使用WITH ENCRYPTION
本站内容用于信息整理与展示,如有侵权或内容问题请及时联系处理。

相关推荐

补充同频道和同主题内容,方便继续浏览更多相关内容。

同类最新

继续查看同栏目最近更新的文章。

更多
Redis 7.0增量AOF重写RDB前导码配置详解
数据库 · 2026-07-02

Redis 7.0增量AOF重写RDB前导码配置详解

先说一个几乎所有人都踩过的典型误区:很多人把 aof-use-rdb-preamble yes 当作开启“增量重写”的开关。实际上,这个配置只干了一件事——让重写后的 AOF 文件头部带上 RDB 快照。它解决的是加载速度问题,跟“增量重写”本身的概念压根不是一回事。真正的增量重写,依赖的是 Red

在Python Tornado异步框架中安全执行SQL命令的方法与最佳实践
数据库 · 2026-07-02

在Python Tornado异步框架中安全执行SQL命令的方法与最佳实践

直接在Tornado里用SQLAlchemy同步执行SQL,结果就是阻塞IOLoop,所谓“异步框架里写同步数据库代码”,等于白搭。安全执行的关键不是“怎么写SQL”,而是“怎么不卡住事件循环”。 为什么不能在RequestHandler里直接调用session execute() 因为sessio

利用SQL触发器实现在INSERT数据时自动同步到审计表
数据库 · 2026-07-02

利用SQL触发器实现在INSERT数据时自动同步到审计表

先说结论:可以用触发器把 INSERT 数据同步到审计表,但必须用 AFTER INSERT,并且审计表的字段顺序、类型、字符集得和源表严格一致。否则,轻则写入错位、数据截断,重则直接报错、丢数据。下面把这些坑一个一个掰开说。 能,但必须用 AFTER INSERT,且审计表字段顺序、类型、字符集要

如何用SQL编写按不同工作日统计员工出勤率
数据库 · 2026-07-02

如何用SQL编写按不同工作日统计员工出勤率

在实际业务中,统计不同工作日的出勤率是HR系统里的高频需求。如果直接按日期函数分组,很容易掉进语言环境、索引失效或分母口径的坑里。下面就来拆解具体的实现要点。 必须用 CASE WHEN 将日期映射为固定 weekday 标签(如 Mon )再分组,避免语言环境导致的分组断裂;需过滤 DOW IN

Spring Boot 3动态拼接SQL为何引发严重安全漏洞
数据库 · 2026-07-02

Spring Boot 3动态拼接SQL为何引发严重安全漏洞

SQL注入漏洞的核心成因,本质上是因为用户输入直接参与了SQL语句的字符串拼接,而未采用参数化绑定机制。在MyBatis中使用${}、QueryWrapper中调用apply()与last()、JPA的@Query注解进行拼接等操作,都会绕过PreparedStatement的安全防护。动态字段必须