
简介面向政务信息化、数据可视化与地理信息系统开发者的行政区划基础数据包聚焦全国五级行政区域名称与区域代码的完整对应关系可支撑地址库建设、区域联动下拉、数据统计归集等常见业务场景。压缩包共3个文件以2个SQL脚本和1个CSV数据表为主SQL文件可直接导入数据库完成建表与数据初始化CSV便于在Excel或脚本中快速查阅核对整体约18.8MB。数据覆盖省级、地级、县级、乡级、村级五个层级包含省、自治区、直辖市、特别行政区地区、盟、自治州、市县、自治县、旗、自治旗、县级市、市辖区、林区乡、民族乡、镇、街道、苏木以及行政村、社区等类型层级划分细致、代码与名称一一对应。已有5021人学习下载适合需要快速获取权威区划底表、减少手工整理成本的开发者直接复用。1. 五级行政区划库到手先别急着建表这份 SQL 资源到底解决什么问题做后台系统的人大概都遇到过这种需求用户注册要选省市区街道订单要按行政区统计报表要按区县汇总。看着简单真动手才发现最麻烦的不是写代码是数据从哪来。网上搜到的行政区划表要么只到区县三级要么是几年前的老数据撤县设区、街道合并全对不上最后只能自己一条条补。这份 2023 年更新的全国五级行政区域数据库表和 SQL 文件就是冲着这个痛点来的——省、市、区县、街道乡镇、村社区五级全覆盖直接给建表语句和 INSERT 数据导入就能用。它适合三类人一是做 SaaS、电商、政务类后台的开发者需要一套稳定的地址选择数据源二是做数据分析和报表的要按行政层级做聚合三是接手老项目、发现地址库残缺需要替换的维护者。下面按「这份库长什么样 → 怎么导进去 → 怎么查得快 → 坑在哪」的顺序拆每一步都能照着复现。2. 拆开 SQL 文件看结构五级编码怎么设计才不返工拿到一个 SQL 文件第一反应不该是直接 source 进去而是先看它的表结构和编码规则。行政区划库最容易翻车的地方就在编码设计上——如果编码规则没吃透后面做层级查询、做增量更新都会卡住。这一章先把文件结构和字段含义讲清楚再谈导入。2.1 表结构与字段含义这类资源通常给一张主表字段大致是这几类行政区划代码、名称、层级、父级代码、城乡分类或排序标记。不同版本字段名会有差异但核心逻辑一致。先看一个典型的建表语句CREATE TABLE region ( code varchar(12) NOT NULL COMMENT 行政区划代码五级统一编码, name varchar(64) NOT NULL COMMENT 行政区名称, level tinyint(1) NOT NULL COMMENT 层级1省 2市 3区县 4街道乡镇 5村社区, parent_code varchar(12) NOT NULL DEFAULT 0 COMMENT 父级行政区划代码顶级为0, full_name varchar(128) DEFAULT NULL COMMENT 全称如XX省XX市XX区, sort int(11) NOT NULL DEFAULT 0 COMMENT 同级排序, PRIMARY KEY (code), KEY idx_parent (parent_code), KEY idx_level (level) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT全国五级行政区划表;这里有几个点值得说清楚。code用 varchar 而不是 int是因为行政区划代码虽然看着是数字但前导零有意义某些层级代码长度也不完全一致用整型会丢零、比较时也容易出问题。level字段是这份库好用与否的关键——有了它查「所有省」就是WHERE level1不用靠代码长度去猜。parent_code建索引是必须的因为地址选择器最典型的查询就是「给我某个父级下的所有子级」没索引在五级数据量下会明显变慢。提示导入前先确认字符集是 utf8mb4。行政区名称里偶尔会有生僻字utf8 三字节存不下导入时会报错或截断。2.2 编码规则与层级关系五级行政区的编码不是随便编的通常遵循「上级代码 本级顺序码」的拼接逻辑。省级两位、市级在省基础上加两位、区县再加两位街道和村社区继续往后接。理解这个规则的价值在于你可以不依赖parent_code直接用code的前缀匹配做层级查询这在某些场景下比递归查父级更快。比如查某个省下的所有区县可以写成-- 假设某省代码为 330000区县层级为3 SELECT code, name FROM region WHERE level 3 AND code LIKE 33%;但要注意前缀匹配依赖编码规则的严格性。如果这份库在街道、村社区层级的编码上做了特殊处理比如部分地区的村社区代码不是纯拼接前缀匹配就会漏数据。稳妥做法是层级查询优先用parent_code关联前缀匹配只作为辅助校验手段。我一般会先跑一条校验 SQL确认编码前缀和 parent_code 关系一致-- 校验子级代码是否以上级代码为前缀排除顶级 SELECT COUNT(*) FROM region c JOIN region p ON c.parent_code p.code WHERE c.code NOT LIKE CONCAT(p.code, %);这条语句返回 0 说明编码规则规整可以放心用前缀返回非 0 就得老老实实走 parent_code。这一步花不了几分钟但能省掉后面排查「为什么某个区县查不出来」的时间。2.3 数据规模与导入前的预检五级数据全量下来村社区这一层数量很大整表几十万行是常态。导入前建议先做两件事一是看文件大小和 INSERT 语句的批量方式二是确认目标库的max_allowed_packet够不够。很多 SQL 文件把大量数据塞进单条 INSERT包太小会直接报错。# 查看文件行数和大小估算导入耗时 wc -l region.sql du -h region.sql # 查看当前包大小限制 mysql -uroot -p -e SHOW VARIABLES LIKE max_allowed_packet;如果文件里是单条超长 INSERT而max_allowed_packet只有默认的 4M导入大概率中断。解决办法要么调大该参数要么用工具把大 INSERT 拆成批量插入。预检这一步别省血泪经验是导入跑到一半报错回滚重来更费时间。3. 导入与验证从 source 到可查询的完整流程结构看明白了接下来是把它真正跑起来。这一章给一套从建库、导入到验证的完整操作顺带说清楚导入方式的选择理由和验证该验什么。很多人导入完就以为完事了结果上线才发现有重复、有孤儿节点所以验证环节必须做。3.1 建库与导入的两种方式导入方式无非两种命令行 source 和图形工具导入。数据量大的时候我倾向命令行可控、能看到报错、方便脚本化。先建库设字符集CREATE DATABASE region_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci; USE region_db;然后执行导入。如果 SQL 文件里已经包含建表语句直接 source 即可mysql -uroot -p --default-character-setutf8mb4 region_db region.sql如果文件只有数据没有建表就先执行第 2 章那份建表语句再导入数据。导入过程中建议加--show-warnings把警告也打出来有些截断问题只报 warning 不报 error不看就漏了mysql -uroot -p --default-character-setutf8mb4 --show-warnings region_db region.sql图形工具导入适合数据量不大、想边导边看的情况但几十万行用工具容易卡界面还是命令行稳。导入完成后先看行数和层级分布确认数据完整SELECT level, COUNT(*) AS cnt FROM region GROUP BY level ORDER BY level;正常情况下五级数量应该是逐级放大的金字塔如果某一级数量明显偏少说明那部分数据没导进去或者被截断了。3.2 完整性验证孤儿节点与重复编码导入成功不等于数据可用。行政区划库最常见的两类脏数据是孤儿节点parent_code 指向的父级不存在和重复编码。这两类问题不查出来地址选择器就会出现「选了市没有区」或者「同一个区出现两次」的玄学现象。查孤儿节点-- 找出父级不存在的记录顶级 parent_code0 除外 SELECT c.code, c.name, c.parent_code FROM region c LEFT JOIN region p ON c.parent_code p.code WHERE c.parent_code 0 AND p.code IS NULL LIMIT 50;查重复编码主键存在的话理论上不会有但导入时若表没建主键就可能出现SELECT code, COUNT(*) AS cnt FROM region GROUP BY code HAVING cnt 1;孤儿节点如果数量很少可能是源数据本身的边界情况比如某些特殊管理区可以人工确认后保留或剔除如果数量多说明导入不完整得重新导。重复编码则必须处理否则关联查询会出笛卡尔积报表数字直接翻倍。3.3 用递归查询验证层级链路验证五级链路是否贯通最直接的办法是从一个村社区往上递归查到省看能不能走通。MySQL 8 支持 CTE 递归写法如下WITH RECURSIVE chain AS ( -- 起点任取一个五级节点 SELECT code, name, level, parent_code FROM region WHERE level 5 LIMIT 1 UNION ALL SELECT p.code, p.name, p.level, p.parent_code FROM region p JOIN chain c ON p.code c.parent_code ) SELECT * FROM chain ORDER BY level;这条查询应该返回 5 行层级从 5 递减到 1。如果中途断了说明链路有缺口。MySQL 5.7 没有 CTE可以用多次自连接或者应用层循环查但数据量大时自连接性能一般建议升级到 8.0 或者用应用层拼。验证通过后这份库才算真正可用。4. 查询性能与索引地址选择器为什么不能只靠 LIKE数据导进去、验证通过接下来就是怎么查得快。地址选择器、报表聚合、模糊搜索是三种典型查询模式各自的优化点不一样。这一章按查询模式拆索引策略顺带说清楚哪些写法看着能跑、实际会拖垮性能。4.1 按父级查子级的索引设计地址选择器最核心的查询是「给我某个父级下的所有子级」对应WHERE parent_code ?。这个查询必须走索引否则每次点开下拉框都全表扫五级数据量下体验会很差。第 2 章建表时已经给parent_code建了普通索引但更优的做法是建联合索引(parent_code, level)因为实际查询往往同时限定层级ALTER TABLE region ADD INDEX idx_parent_level (parent_code, level);联合索引的顺序有讲究parent_code放前面是因为它的区分度更高先缩小范围再按 level 过滤。如果反过来把 level 放前面低层级的 level 值区分度低索引效果会打折。建完可以用 EXPLAIN 确认EXPLAIN SELECT code, name FROM region WHERE parent_code 330100 AND level 3;看key列是否命中idx_parent_levelrows是否明显小于全表行数。如果没命中检查是不是查询条件类型不匹配比如 parent_code 传了数字导致隐式转换。4.2 模糊搜索的边界与替代方案用户输入「西湖」想搜到所有含「西湖」的行政区这种需求很常见但LIKE %西湖%前置通配符用不上索引数据量大时就是全表扫。常见做法有几种一是限制搜索层级只搜省市区不搜村社区把扫描范围压下来二是用全文索引或者搜索引擎三是预先把常用搜索词做成缓存。如果坚持用 LIKE至少加上层级限制别在五级全量上扫-- 只在省市区三级里模糊搜范围小很多 SELECT code, name, level FROM region WHERE level 3 AND name LIKE %西湖%;注意前置通配符的 LIKE 在几十万行上单次可能还能忍但并发一上来就是灾难。地址搜索这种高频操作要么加缓存要么上专门的搜索组件别指望数据库硬扛。4.3 报表聚合的预计算思路按行政区做统计报表时经常需要「某省下所有订单数」这就涉及把子级数据往上汇总。如果每次查询都递归展开子级再聚合性能很难看。常见做法是预计算在业务表里冗余存储省、市、区县代码写入时一次性填好查询时直接按冗余字段 GROUP BY避免运行时递归。-- 业务表冗余行政区层级字段的示例 ALTER TABLE orders ADD COLUMN province_code varchar(12) DEFAULT NULL, ADD COLUMN city_code varchar(12) DEFAULT NULL, ADD COLUMN district_code varchar(12) DEFAULT NULL;冗余字段的代价是写入时要多查一次行政区库把层级补全但换来的是报表查询不用递归。这个取舍在报表场景下通常划算。如果不想改业务表也可以建一张行政区层级映射表把每个节点到各级祖先的对应关系预先算好查询时直接 JOIN效果类似。5. 避坑与排查导入和使用中最容易翻车的五件事前面几章讲的是顺路径这一章专门讲翻车现场。下面五条都是实际导入和使用这类行政区划库时反复出现的问题每条按「现象 → 原因 → 解决」写遇到对号入座即可。5.1 导入报错「Data too long」或中文变问号现象导入到一半报字段超长或者导完后名称全是问号。原因通常是字符集不匹配——文件是 utf8mb4目标库或连接却是 latin1/utf8。解决建库时显式指定DEFAULT CHARACTER SET utf8mb4导入命令加--default-character-setutf8mb4连接串也确认字符集一致。三处都对齐才不会出问题。5.2 导入中断但没报错行数对不上现象命令跑完了但COUNT(*)比预期少。原因多半是单条 INSERT 超过max_allowed_packetMySQL 静默截断或客户端提前断开。解决调大max_allowed_packet比如 64M或者用脚本把大 INSERT 拆成每批 500 行的批量插入。导入后务必按层级统计行数做核对。5.3 地址选择器出现「选了市没有区」现象前端联动时某个市下面查不到区县。原因通常是孤儿节点——该市的区县数据缺失或者 parent_code 写错。解决跑第 3 章的孤儿节点查询定位缺失的父级如果是源数据本身缺需要从其他渠道补齐别硬编码兜底。5.4 同一行政区出现两次导致统计翻倍现象报表数字比实际大一倍。原因表没建主键或唯一索引导入时重复插入。解决先查重复编码去重后给code加主键或唯一索引从根上杜绝重复导入。重复导入前先TRUNCATE或加INSERT IGNORE。5.5 递归查询报错「Recursive query aborted」现象MySQL 8 跑递归 CTE 时报超过最大递归深度。原因数据里存在环形引用A 的父级是 BB 的父级又是 A递归出不来。解决先查环形引用把parent_code指向自身或形成环的记录修掉临时可以把cte_max_recursion_depth调大但治标不治本根上还是得清脏数据。6. 增量更新与版本管理老库怎么平滑换到 2023 版行政区划不是一成不变的撤县设区、街道合并每年都有。手里已经有一套老库的直接整表替换风险大——业务表里存的老编码可能失效。这一章讲怎么把 2023 版平滑换上去以及日常怎么维护版本。6.1 新旧库比对找出变更集先建一张新库表比如region_2023把新版数据导进去然后和老表做全量比对找出新增、删除、改名的记录-- 新增的编码新库有、老库没有 SELECT n.code, n.name, n.level FROM region_2023 n LEFT JOIN region_old o ON n.code o.code WHERE o.code IS NULL; -- 已删除的编码老库有、新库没有 SELECT o.code, o.name, o.level FROM region_old o LEFT JOIN region_2023 n ON o.code n.code WHERE n.code IS NULL; -- 名称变更的编码 SELECT o.code, o.name AS old_name, n.name AS new_name FROM region_old o JOIN region_2023 n ON o.code n.code WHERE o.name n.name;比对结果决定迁移策略新增的直接插入删除的要评估业务表里是否还有引用改名的更新名称即可。编码本身变更的情况最麻烦需要业务表同步改这类通常很少但要单独拎出来处理。6.2 用映射表处理编码失效对于已删除或编码变更的节点业务表里的历史数据不能直接丢。常见做法是建一张新旧编码映射表把老编码映射到新编码合并的映射到合并后的节点撤销的映射到上级查询时通过映射表转换CREATE TABLE region_code_mapping ( old_code varchar(12) NOT NULL, new_code varchar(12) NOT NULL, remark varchar(128) DEFAULT NULL, PRIMARY KEY (old_code) );这张表不用全量建只维护有变更的那部分。历史订单按老编码统计时JOIN 一下映射表就能归到新行政区下报表口径不会断。这个习惯我建议每个维护行政区库的项目都养成不然每次更新都是一次数据考古。6.3 版本字段与定期同步如果项目长期运行建议在行政区表里加一个version字段标记数据版本比如2023同时在配置里记录当前使用的版本号。每次更新时先比对新版本号避免重复导入。定期同步的频率看业务敏感度一般一年一次全量更新够用涉及区划调整频繁的地区可以半年一次。ALTER TABLE region ADD COLUMN version varchar(8) NOT NULL DEFAULT 2023;更新流程固定成三步导新库 → 跑比对 → 按变更集更新主库并维护映射表。这套流程走顺了以后换任何一年的版本都是同样的操作不用每次重新摸索。从那以后我每次换行政区库都强制先跑一遍比对和孤儿节点校验再动主库——数据这东西后悔药不好买。希望这份拆解能帮到你把这份 2023 版五级行政区划库顺利落到自己的项目里。本文还有配套的精品资源点击获取