五级行政区划SQL数据:结构解析与MySQL导入实战指南
2026/9/7 4:32:12 网站建设 项目流程

简介:2018年最新全国省市、区、县、镇、乡五级行政区域完整SQL数据,面向数据库开发者、GIS分析师和数据分析人员,可用于构建地址联动选择、区域统计与地理信息系统(GIS)底层数据库,解决行政区域层级多、数据分散难维护的痛点。压缩包内含2个SQL文件,共8.63MB,主文件包含完整的建表语句和全部省市区县镇乡数据,辅助文件可作为备份或补充脚本参考。数据包含地区编码、名称、父级编码和等级等关键字段,其中地区编码遵循GB2260标准,父级编码清晰构建了五级层级关系,导入MySQL等关系型数据库后,即可实现省、市、区、县、镇、乡的逐级联动查询。实际可用于电商收货地址、物流配送、人口统计、区域经济分析等场景,也可作为GIS系统的底层地理字典,大幅节省基础数据的整理和录入时间。目前已有529人学习下载,适合需要快速获取规范行政区划数据的开发者和研究者。 前阵子整理旧硬盘,翻出一个2018年保存的压缩包,标题写着“2018最全国省市、区、县、镇、乡5级完整SQL最新版”。这份文件跟了我好几个项目,从后台管理系统的省市区三级联动,到电商的配送区域配置,再到数据报表里按区域维度做汇总分析,几乎每次都能派上用场。虽然现在回头看“最新版”三个字有点年代感,但行政区划SQL数据放到今天依然是很多开发场景里的刚需。这篇文章把这套数据从结构、导入到排坑的完整玩法拆开讲讲,适合正在做后台开发、需要省市区联动、或者刚开始接触行政区划数据的同学参考。

1. 五级行政区划数据,到底在解决什么问题

1.1 开发场景里绕不开的行政区划需求

只要产品沾一点地域属性,行政区划数据就躲不开。最典型的场景是电商平台的下单页面,用户选完省、市、区县之后,系统要能根据所选区域自动匹配配送范围或运费模板;后台管理系统里的组织架构、门店管理,通常也按省市县来分层;到了数据分析侧,销售报表、用户分布、渠道统计这些需求,更是离不开一套完整的区域维度表。

这个需求听起来简单,真做起来往往很折腾。网上能搜到很多JSON格式的省市区数据,但格式五花八门,有的只有两级,有的把乡镇一级砍掉了,有的城市编码根本对不上国家标准。更麻烦的是,如果系统部署在离线环境,外部的API再方便也用不上。相对靠谱的办法,就是找一份结构规范的行政区划SQL数据,直接导入数据库变成一张正规的表,后续不管是做级联查询、做JOIN关联、还是生成树结构,都在数据库里搞定,快而且可控。

1.2 2018版的数据放到现在还有多大价值

你先别急着嫌这个版本老。行政区划代码整体上非常稳定,尤其是省级和地级,很多编码用了十几年都没变过。大型调整通常发生在乡镇一级,比如撤乡并镇、街道撤并这类操作,变动相对频繁,但对大多数业务系统来说,这种变化并不会导致历史数据失效——用户的收货地址已经存成文本了,历史订单的区域编码保留旧值是合理的,新的录入需求再去做增量调整就行。

所以这套2018年数据最合适的定位是“基准数据”。拿来做系统开发、功能演示、离线环境部署、或者作为套用真实数据结构的样例,都非常顺手。如果业务对区划的时效性要求特别高,比如办理政务、物流分拣这类场景,那就需要在此基础上做增量更新。我个人的经验是:把2018版当作地基,后续用官方区划变更公告去补丁式升级,比直接换一套全新数据要稳得多,因为你清楚改动点在哪里。

2. 先弄懂五级结构,再动手导数据

2.1 拿到SQL文件,第一件事永远是看表结构

很多同学下载完文件就直接source导入,结果报错或者查出来的数据不对,最后才发现字段名和预想的不一样。行政区划SQL这类资源,网上流传的版本特别多,字段命名没有统一标准,有的用id、pid,有的用code、parent_code,有的还带了short_name、full_name、post_code这类附加字段。

我拿到这份2018版文件,先看了它开头一段建表语句,结构整理出来大概是这样的:

CREATE TABLE `sys_area` ( `id` int(11) NOT NULL AUTO_INCREMENT, `code` varchar(12) NOT NULL COMMENT '区划代码', `name` varchar(100) NOT NULL COMMENT '行政区划名称', `level` tinyint(4) NOT NULL COMMENT '层级:1省 2市 3区县 4镇乡 5村社区', `parent_code` varchar(12) DEFAULT NULL COMMENT '父级区划代码', `short_name` varchar(50) DEFAULT NULL COMMENT '简称', PRIMARY KEY (`id`), KEY `idx_code` (`code`), KEY `idx_parent` (`parent_code`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

这里有几个设计上的细节值得注意。code字段用varchar而不是int,是因为区划代码自带前导零,比如北京市东城区是110101,如果用int存储,前导零会直接丢成110101,虽然数字看起来一样,但在按规则取前两位、前四位做统计时就会出问题。parent_code指向父级代码,通过这个字段可以把整张表串成一棵区域树。level字段则明确标识每一行的层级,查询时不用递归也能直接按level过滤。如果项目需要生成省市区级联菜单,这种结构写起SQL来非常顺手。

2.2 区划代码里的编码规律,看懂它你就懂了大半

中国行政区划代码遵循国标体系,核心规律是:省级代码2位,地级代码4位,县级代码6位,乡镇级代码9位,村社区级代码12位。每个下级代码都完整包含上级代码。举个例子,110101东城区,前两位11代表北京市,前四位1101代表北京市辖区,加上最后两位01就是东城区。到了乡镇一级会扩展到9位,110101001可能是某个街道,再往下12位就具体到村或社区。

这个编码规律特别有用。你在业务里拿到任何一个地理位置信息,只需要按位截取,就能反推出它所属的上级区域。比如数据表里只存了用户完整的12位区划代码,做省级汇总时直接用LEFT(code, 2)做GROUP BY,不需要关联区域表,效率非常高。我做过一个区域销售报表,就是用这种方式把几百万条订单按省、市、区县分别汇总,一条SQL三分钟跑完。

发现文件里如果缺少level字段,你甚至可以按照编码长度推断层级:code长度为2是省级,4是地级,6是区县,9是乡镇,12是村社区。这一点在数据清洗、格式校验时也是重要的判断依据。不过要提醒一下,有些地区的区划代码存在历史遗留情况,比如省直辖县级市、直辖市下面直接挂区等等,这类特殊数据可能出现编码长度不符合常规的情况,不能把规则当成绝对标准。

2.3 怎么判断一份行政区划数据够不够“全”

标题里写着“最全”,但“全”其实是个相对概念。拿到数据后,别只看标题,先做一次分布统计,心里立刻就有数了:

SELECT level, COUNT(*) AS cnt FROM sys_area GROUP BY level ORDER BY level;

我这份2018数据跑出来的结果大致是这样的:

level数量范围说明
134省级行政区,含省、自治区、直辖市、特别行政区
2330左右地级市、地区、自治州
32850左右区、县、县级市
439000左右镇、乡、街道
5视资源而定村、社区,很多版本没有这一层

如果只有前4层,其实是很多公开资源的常态,因为村社区这一级数据量太大,超过60万条,能完整维护下来的资源很少。对大部分面向C端的应用来说,省、市、区县、乡镇街道这4级已经足够用了。关键是导入前想清楚自己的业务到底需要几级,别把“5级”当成硬性指标。

另外要检查一下有没有“孤儿数据”,也就是parent_code指向的上层节点不存在的情况。用下面这条SQL就能查出来:

SELECT a.code, a.name, a.parent_code FROM sys_area a LEFT JOIN sys_area p ON a.parent_code = p.code WHERE a.level > 1 AND a.parent_code IS NOT NULL AND p.code IS NULL;

如果返回结果很多,说明这份数据在整理时就没处理好父子关系,后续做级联查询会遇到很头痛的空节点问题。

3. MySQL导入完整实操:从命令行到验证

3.1 导入之前必须做的三个准备

拿到SQL文件别急着导,先做三个动作能帮你避免八成的问题。

先看文件本身的编码格式。Windows下用Notepad++或VS Code打开看右下角编码,Linux环境可以直接用file命令:

file area_2018.sql

如果是UTF-8编码,导入时指定--default-character-set=utf8mb4;如果是GBK编码,对应的就要用gbk。字符集搞错,中文导入后就是一片乱码或者直接报错。

然后创建目标数据库。建议库、表都明确指定字符集,避免依赖服务器全局配置:

CREATE DATABASE IF NOT EXISTS area_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE area_db;

最后检查目标表是否已存在。如果旧表里有数据,要么先备份,要么按需求清空。千万不要直接拿旧表结构和新文件的结构混在一起,字段对不上会导出一堆意想不到的错误。

3.2 两种导入方式,记熟一条就够用

MySQL导入SQL文件最常用的方式有两种。第一种是在mysql命令行内执行source命令:

mysql -uroot -p --default-character-set=utf8mb4

进入交互界面后:

USE area_db; SOURCE /path/to/area_2018.sql;

source命令的路径在Windows下要注意斜杠方向,建议直接丢到C盘根目录或者一个无中文的路径下,格式比较省心。第二种方式是直接用系统命令重定向:

mysql -uroot -p --default-character-set=utf8mb4 area_db < /path/to/area_2018.sql

这两种方式本质一样,选自己习惯的就行。整个导入过程非常快,文章里这份数据实际只有4万多条,用不到一秒钟就执行完了。

如果你用的不是MySQL,思路完全一致。SQL Server里可以用sqlcmd -S server -U sa -P pwd -d area_db -i area.sql,或者直接在SSMS里打开文件执行;PostgreSQL可以用psql -U user -d area_db -f area.sql。最核心的准备工作永远是字符集和表结构。

3.3 导入完成后,花一分钟做验证

导入不等于成功,很多问题是导入之后才暴露的。我习惯性地做三步验证。

第一步,统计总行数和各层级行数,与预期范围对比:

SELECT COUNT(*) FROM sys_area; SELECT level, COUNT(*) FROM sys_area GROUP BY level;

第二步,抽查几条数据,确认代码长度、名称、层级都正常:

SELECT code, name, level, parent_code FROM sys_area WHERE level = 1; SELECT code, name, level, parent_code FROM sys_area WHERE code = '110101';

第三步,确认关键索引存在。如果原文件里没有建索引,这一步必须补上:

ALTER TABLE sys_area ADD UNIQUE INDEX uk_code (code); ALTER TABLE sys_area ADD INDEX idx_parent (parent_code);

code字段加唯一索引,后续写入新数据时不会出现重复;parent_code加普通索引,所有按父级查询的操作都会受益。这套索引组合在4万多行的表上体感可能不大,但如果未来数据量增长到百万级别,区别就非常明显了。

4. 实际使用中躲不开的坑与排查清单

4.1 中文乱码:九成是字符集没对齐

遇到过太多次“导入后中文全是问号”的求助,点名批评最多的就是charset三部曲没做对。记住一个结论:SQL文件本身是什么编码,建表时用什么字符集,导入时指定什么字符集,这三个地方必须保持一致。

实际操作中,如果SQL文件开头写了SET NAMES utf8mb4,那建表和连接的字符集也要用utf8mb4。如果文件是GBK编码但建表用了utf8mb4,就会出现导入报错或者乱码。最简单的处理是:先看文件编码,然后把整个链路统一到utf8mb4,这也是目前最推荐的字符集,对生僻字和emoji都友好。Windows环境下从Excel复制数据容易出现GBK混入的情况,这点要额外留意。

我遇到过一种更难察觉的乱码,不是中文变问号,而是“绮剧爌”这样的中文,原因是SQL文件本身是GBK,但客户端把内容当成了UTF-8来显示。这种属于显示层问题,不影响数据本身,不过如果数据要导出给第三方,最好先统一转码再交付。

4.2 关联查询查不出数据的几个隐性坑

层级数据最经典的坑,是父子关联不上。虽然用LEFT JOIN检查孤儿数据能定位问题,但有些情况就算parent_code对得上,查询结果依然不对,这时候大概率是这几个原因。

第一,parent_code为空。省一级的parent_code往往是null或0,如果你写关联条件时用了ON a.parent_code = p.code,这一层会全部被过滤掉。处理时需要加上OR a.parent_code IS NULL之类的判断,或者查询首层时直接按level=1过滤,不依赖parent_code。

第二,编码含前导零。如果某一步ETL把code字段转成了int,那么110101会变成110101,看起来没区别,但如果有一批数据被拆成了11、1101、110101这种短代码,关联逻辑就会出错。所以一旦用编码做关联,字段类型必须是varchar。

第三,隐藏字符。SQL文件里偶尔会带上换行符、回车符、BOM头,肉眼根本看不出来。遇到关联不上的情况,可以用SELECT code, HEX(code) FROM sys_area WHERE code = '110101'看看实际字节,或者用TRIM()清洗一遍再关联。

4.3 旧版数据升级替换的正确姿势

当你拿到更新的行政区划数据,要替换旧表时,千万别直接DROP TABLE,更别在还有外键引用的情况下乱动。推荐的流程是:

先备份旧表,导出成文件或者再复制一张表。然后做一个版本的标记,比如在sys_area表旁边建一个data_version表,记录“2018基础版本”“2024增量补丁”之类的更新日志,这样后续出问题能快速回溯。

如果新SQL文件自带DROP TABLE IF EXISTSCREATE TABLE,导入时它会先把旧表删了再建,这其实是最省事的。如果文件只包含INSERT语句,那就需要手动清空旧数据再导入,避免主键冲突:

TRUNCATE TABLE sys_area; SOURCE /path/to/new_area.sql;

这里要注意TRUNCATE不可回滚,操作前确保已经备份。很多人图省事用DELETE FROM sys_area,大表上效率低且自增ID不会重置,TRUNCATE是更合理的选择。

4.4 常见问题速查表

现象可能原因解决办法
中文全部为问号文件、库表、导入连接字符集不一致统一使用utf8mb4,重新导入
导入报错syntax errorSQL文件编码与选定的字符集不匹配确认编码后用对应字符集导入
省级节点查不到关联条件过滤掉了parent_code为空的数据按level过滤首层,或允许NULL关联
区划代码前导零丢失code字段被存成了int改为varchar(6)或varchar(12)
父子关联不完整数据本身有孤儿节点用LEFT JOIN定位,补齐或标记异常数据
重复导入主键冲突未清空旧表先TRUNCATE再导入
想替换但怕丢数据缺少备份意识先导出旧表,再执行替换操作

5. 进阶技巧:一条SQL把五级区域串成完整路径

5.1 用递归查询生成完整层级链

很多业务场景里,你最终要展示的不是单个level的数据,而是“广东省 / 深圳市 / 南山区 / 粤海街道”这样一条完整路径。MySQL 8.0及以上版本支持递归CTE,用一条SQL就能把整个树形结构查出来:

WITH RECURSIVE area_tree AS ( SELECT code, name, level, parent_code, CAST(name AS CHAR(500)) AS full_path FROM sys_area WHERE level = 1 UNION ALL SELECT a.code, a.name, a.level, a.parent_code, CONCAT(t.full_path, ' / ', a.name) FROM sys_area a INNER JOIN area_tree t ON a.parent_code = t.code ) SELECT code, name, level, full_path FROM area_tree;

这段SQL的核心逻辑是先把省级节点作为递归起点,然后不断用parent_code等于上一级code的条件往下扩展,每次拼上当前区域名称。运行结果会生成一个带完整路径的视图,可以直接用在报表、导出、或者前端展示上。

如果你用的是老版本MySQL或者MariaDB 10.2以下,递归CTE用不了,那就只能靠多次LEFT JOIN。因为行政区划的层级是固定的,最多join四到五次:

SELECT p.name AS province, c.name AS city, d.name AS district, s.name AS street FROM sys_area s LEFT JOIN sys_area d ON s.parent_code = d.code LEFT JOIN sys_area c ON d.parent_code = c.code LEFT JOIN sys_area p ON c.parent_code = p.code WHERE s.level = 4;

这种方式虽然SQL写起来长一点,但兼容性极好,读取性能也不差。

5.2 千万级业务表关联区域数据时,先按前缀聚合

最后一个想分享的技巧,和性能有关。行政区划表本身很小,但业务表可能几千万行。如果每次统计都要JOIN区域表,即使有索引也还是有压力。更聪明的做法是直接利用编码前缀做聚合分析。

比如订单表里存了用户的12位区划代码user_area_code,想按省统计订单量:

SELECT LEFT(user_area_code, 2) AS province_code, COUNT(*) AS order_cnt FROM orders GROUP BY LEFT(user_area_code, 2);

如果还需要省市两级维度的下钻分析,可以用LEFT(user_area_code, 2)LEFT(user_area_code, 4)分别放在GROUP BY里,再通过一次关联把代码翻译成名称。这样省去了大量JOIN开销,整条SQL的执行计划会清爽很多。

不过要注意,只有12位完整编码才能这样直接截取,如果业务表里存的是低层级编码,截取出来的前缀不一定对应正确的上级。这种情况下还是老老实实JOIN区域表,别为了一点性能牺牲准确性。

写在最后的一点经验

用这套行政区划数据这几年,我最大的体会是:数据要当成会过期的东西来管理,而不是当成不可变的字典。所谓“最新版”,永远只是某个时间点的快照。拿到任何行政区划SQL,第一件事就是确认它的版本边界和数据范围,第二件事是设计好升级替换的流程,第三件事才是去关心查询怎么写。

如果让我给一个最实用的建议,那就是下载后先把原始文件、导入日期、数据来源写进一个README,和数据文件放一起。半年后你回来看这个目录,能省下大量确认数据时效性的时间。我自己就吃过亏,有一次项目上线前才发现用的是过时数据,用户选了已经撤销的乡镇,售后问题一堆。现在所有行政区划数据入库前,都必须带有明确的版本标记和更新日志。

希望这篇关于行政区划SQL数据的拆解对你有用,如果你在导入或使用过程中遇到其他奇怪的问题,欢迎按文里的排查思路先自查一遍,大部分坑都跑不出这几个方向。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询