简介:面向从事地理信息、人口统计、物流规划与数据库开发的人员,这份GB/T 2260全国行政区划代码数据集提供了一套标准化的四级区划编码参考,覆盖省/直辖市、市、县及乡镇街道等层级,可用于搭建行政区划表、做数据匹配与地域维度分析。压缩包共4个文件,包含SQL脚本、Access数据库、TXT文本和XLSX表格,整体约5.01MB;SQL脚本便于导入MySQL建表,Access文件可脱离SQL环境直接查看,TXT与XLSX则方便在Excel中筛选、核对和二次整理。数据量约46462条,版本为2017年最新版,能支撑人口统计、GIS、物流规划与市场分析等场景,也适合作为开发测试的基础字典表。使用时需留意行政区划调整带来的时效性。目前已有1105人学习。
1. 项目定位:一份 GB/T 2260 行政区划代码 SQL 能省掉多少脏活
做后台系统的都躲不开"选省市区"这个需求:用户注册要选、订单要按地区统计、门店要挂行政区划编码。第一反应是找接口,接口要授权、有限流、赶上对方机房抖动还得背锅;第二反应是自己维护一张表,于是就有了无数个"从网页上扒下来再手工补"的下午,补完之后发现某个县级市根本不存在。这份资源解决的就是最底层那一层:把 GB/T 2260 行政区划代码整理成可直接导入关系型数据库的 SQL 脚本,省、地、县三级齐全,字段带名称、层级、父级编码,导进去就能查,不用再对着标准文本一行行敲。适合做 SaaS 多租户地域配置的、做物流地址库的、做数据看板按地区聚合的,以及任何不想为了"选个省"再引一个第三方 SDK 的团队。数据是静态的,但静态恰恰是它的价值——你把它当成字典表放进自己的库,主动权就在自己手上。
2. 数据骨架:六位编码规则与省市区建表语句怎么对应
2.1 GB/T 2260 的六位编码到底怎么切
行政区划代码是 6 位定长数字字符串,不是整数,这个细节决定了后面所有字段类型的选择。结构上分三段,每段两位:
| 位段 | 含义 | 取值说明 |
|---|---|---|
| 第 1-2 位 | 省级 | 01-99 之间分配,用于标识省、自治区、直辖市 |
| 第 3-4 位 | 地级 | 00表示该省直接管辖县级单位,01-99 为地级市、自治州、地区 |
| 第 5-6 位 | 县级 | 00表示该地级单位直接管辖,01-99 为市辖区、县、县级市 |
一个省级单位的代码形如900000,它下辖的地级单位就是900100、900200、900300这样按顺序往后排;一个地级单位的代码形如900100,它下辖的区县则是900101、900102。所以严格意义上说,"省级代码"就是后四位全是00的代码,"地级代码"就是后两位是00的代码,这个规律可以直接用来校验数据是否自洽。很多数据包里没有单独的 level 字段,靠的就是这条规则现算。
值得单独拎出来说的是直辖市和特殊县级单位。直辖市的省级代码下面通常不会直接挂区县,而是先挂一层"市辖区"当作伪地级,中间两位用01、02之类;而省直辖的县级行政单位,中间两位常见90这样的特殊段,父级直接指回省级代码。这两种形态是后面查询断链的主要来源,建表的时候必须留有兜底余地。
解析一个代码属于哪一层,写个小函数就够了:
def parse_code(code: str) -> dict: """把 6 位行政区划代码拆成省/市/县三级""" if not code or len(code) != 6 or not code.isdigit(): raise ValueError(f"非法区划代码: {code}") if code[2:] == "0000": level = 1 # 后四位全 0,省级 elif code[4:] == "00": level = 2 # 后两位全 0,地级 else: level = 3 # 其余都是县级 return { "province": code[:2] + "0000", # 省级代码 "city": code[:4] + "00", # 地级代码 "county": code, # 县级代码 "level": level, }parse_code的入参必须是字符串而不是整数,因为01这种省级代码一旦被当成整数解析,前导零直接消失,整个层级关系就废了。返回值里的province和city是向上补齐的结果,拿来反查父级最方便,level则用于判断这条记录该落在哪一级。
另一个高频混淆点是 6 位行政区划代码和 12 位统计用区划代码的区别。后者是统计口径,前 6 位与行政区划代码对齐,后 6 位是居委会、村委会的标识,长度和含义都不同。有人把 12 位数据直接塞进 6 位字段,结果被截断得一塌糊涂。做业务地址库时用 6 位就够,只有做人口普查级别的统计才需要 12 位。
2.2 一张自关联表还是拆成三张表
拿到 SQL 包之后第一个决策是表结构。常见三种形态,各有取舍:
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 单表自关联(code + parent_code + level) | 结构简单,增删改只动一处,天然支持任意层级扩展 | 三级联动要自连接两次或用递归 CTE | 绝大多数业务系统 |
| 拆成 province / city / county 三张表 | 查询直观,前端字段对接直白 | 数据冗余,层级调整要同时改三张表 | 层级绝对固定、只读的展示场景 |
单表 + 物化路径(path 存900000/900100/900101) | 查某节点的整棵子树一条 LIKE 搞定 | 路径维护成本高,任何父级变更都要级联更新 | 需要频繁查下级的组织架构类系统 |
我一般会选第一种,单表自关联。理由很实在:行政区划虽然看着是三级,但业务上经常出现"省直辖县级单位"这种跨级挂载,三表模型的county.city_code就得填空值,查询时到处写IS NULL判断,血泪经验。单表只多一个 level 字段,什么形态都能容纳。
对应的建表语句:
CREATE TABLE `sys_region` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键', `code` CHAR(6) NOT NULL COMMENT 'GB/T 2260 六位行政区划代码', `name` VARCHAR(64) NOT NULL COMMENT '行政区划全称', `short_name` VARCHAR(32) DEFAULT NULL COMMENT '简称,去掉省/市/县等后缀', `parent_code` CHAR(6) DEFAULT NULL COMMENT '父级代码,省级为 000000', `level` TINYINT NOT NULL COMMENT '层级 1省 2市 3县', `pinyin` VARCHAR(64) DEFAULT NULL COMMENT '拼音全拼,用于模糊检索', `initial` CHAR(1) DEFAULT NULL COMMENT '拼音首字母,用于城市选择器索引', `status` TINYINT NOT NULL DEFAULT 1 COMMENT '1有效 0已撤销或合并', PRIMARY KEY (`id`), UNIQUE KEY `uk_code` (`code`), KEY `idx_parent` (`parent_code`), KEY `idx_level_name` (`level`, `name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='行政区划代码表';code用CHAR(6)而不是INT,这是整张表最不能妥协的地方,前导零丢一次就得全表重导。name给到 64 是因为确实存在长度夸张的自治州全称,32 位会截断。short_name是给前端展示用的,列表页放全称太挤。status字段很多数据包里没有,建议自己加上,区划合并撤销是常态,历史订单引用的旧代码不能直接删,标个失效最省心。pinyin和initial也是数据包之外的补充,前端城市选择器要靠首字母索引,导入后跑个脚本批量生成一次就行。
uk_code唯一索引是数据质量的最后一道闸,重复代码在导入阶段就该被拦下来,别等到业务层查出两条同名记录再去排查。idx_parent服务于三级联动,idx_level_name服务于后台按名称搜索,两个索引基本覆盖日常查询。
2.3 包里通常有哪些文件、各自干什么用
这类资源一般会按数据库方言拆成几份,常见构成是:一份 MySQL 版的全量脚本,包含建表语句加批量 INSERT;一份 PostgreSQL 版,可能用COPY语句做高速导入;部分还会附一份 SQLite 文件,拿到手直接能开。文件命名通常带年份或版本号,方便区分数据截止时间。
用之前先确认三件事:建表语句里的字段类型是不是CHAR(6)、INSERT 是不是批量合并成多值形式、脚本开头有没有DROP TABLE IF EXISTS。批量 INSERT 对导入速度影响极大,一条一条插几千行要跑几分钟,合并成INSERT INTO ... VALUES (...),(...),(...)之后通常几秒就完。DROP TABLE存在于全量包里是正常的,但如果你的库里有自己加的字段,直接执行会把它们一起清掉,所以正式环境导入前先看一下脚本头部,把它改成CREATE TABLE IF NOT EXISTS或者先备份。
3. 落库实战:SQL 脚本导入、三级联动查询与索引调优
3.1 三种导入姿势,各自适合什么场合
MySQL 场景下最直接的是命令行重定向:
# 指定字符集,避免中文变成问号 mysql -u root -p \ --default-character-set=utf8mb4 \ mydb < region_gbt2260.sql--default-character-set=utf8mb4这个参数不能省。客户端默认字符集如果是latin1,脚本里的中文会被按错误编码解释,导入完成后name字段整列都是问号或者乱码方块,而且这种损坏是不可逆的,只能删表重来。mydb要提前建好并确认 collation 是utf8mb4_general_ci或utf8mb4_0900_ai_ci,库的字符集和客户端字符集不一致一样会出问题。
如果脚本太大或者需要边导边看报错,进 MySQL 交互界面用source更灵活:
-- 先切库,再执行脚本,报错会逐条打印出来 USE mydb; SET NAMES utf8mb4; SOURCE /path/to/region_gbt2260.sql;SET NAMES utf8mb4的作用和命令行参数一致,只是改成在会话里设置。SOURCE后面必须是服务器能访问到的绝对路径,写成相对路径经常报Failed to open file,很多人卡在这一步以为是文件权限问题。
PostgreSQL 和 SQLite 的处理方式略有差异。PostgreSQL 用psql -f region.sql -U user -d mydb,如果脚本里是COPY ... FROM stdin的形式,必须用psql执行,不能走图形客户端,否则会报"COPY 数据必须来自标准输入"。SQLite 最简单,sqlite3 region.db < region.sql或者进交互界面.read region.sql都行,注意 SQLite 的类型系统是动态的,CHAR(6)只是声明,写入时不会强制截断,所以导入后仍然要用 SQL 校验一遍长度。
导入完先跑一条计数确认:
SELECT level, COUNT(*) AS cnt FROM sys_region GROUP BY level ORDER BY level;省级通常在 30 到 40 之间,地级在 300 到 400 之间,县级在 2800 到 3000 之间。这三个数字记在脑子里,是判断数据完不完整的第一个信号。数量对不上,后面所有查询都白搭。
3.2 三级联动的几种写法与性能差异
最朴素的做法是前端一次性拉全量,后端一条 SQL 返回所有有效记录,前端自己按parent_code组装成树:
SELECT code, name, parent_code, level FROM sys_region WHERE status = 1 ORDER BY level, code;三千行左右的数据,JSON 序列化后大概几百 KB,gzip 之后一百多 KB,首次加载拉一次存前端内存,后续切换省市完全不发请求。这是最省事也最快的方案,适合管理后台这种对首屏体积不敏感的场景。参数上status = 1过滤掉已撤销的区划,ORDER BY level, code保证返回顺序稳定,前端组树时不用再排序。
移动端或者接口不能返大对象时,改成按需查询:
-- 查某个省下面所有地级单位 SELECT code, name FROM sys_region WHERE parent_code = '900000' AND status = 1 ORDER BY code; -- 查某个地级单位下面所有区县 SELECT code, name FROM sys_region WHERE parent_code = '900100' AND status = 1 ORDER BY code;这里有个坑:省直辖县级单位的parent_code直接指向省级,所以用第一条 SQL 查某省的下级,返回结果里会混着地级市和省直辖县。前端如果无脑当成"市"渲染,用户就会看到一个县级单位出现在市级下拉框里。解决办法是在查询里带level字段,前端按 level 分流;或者干脆把省直辖县挂到一个虚拟的市级节点下面。
需要查整棵子树时用递归 CTE,MySQL 8.0 以上和 PostgreSQL 都支持:
WITH RECURSIVE region_tree AS ( -- 锚点:从根节点开始 SELECT code, name, parent_code, level, 1 AS depth FROM sys_region WHERE code = '900000' UNION ALL -- 递归部分:逐层向下找子节点 SELECT r.code, r.name, r.parent_code, r.level, t.depth + 1 FROM sys_region r JOIN region_tree t ON r.parent_code = t.code WHERE r.status = 1 AND t.depth < 5 -- 限制递归深度,防止脏数据死循环 ) SELECT * FROM region_tree ORDER BY code;depth < 5这个限制不是多余的。如果数据里存在 A 的父级是 B、B 的父级又是 A 这种脏数据,没有深度限制的递归会一直跑下去把连接池耗干。有status过滤也一样,脏数据不一定 status 不对。加上限之后最多扫五层,配合code排序输出,结果直接就是树的前序序列。
按名称模糊搜索是另一个高频需求,后台经常要让运营输入"某某县"直接跳转:
SELECT code, name, level, parent_code FROM sys_region WHERE name LIKE CONCAT('%', '关键字', '%') AND status = 1 LIMIT 20;LIKE前置通配符会让idx_level_name索引失效,变成全表扫描。三千行的表扫一遍也就一两毫秒,完全能接受,但如果这张表被其他业务表 join 了几十万次,就该考虑把搜索单独拆出去,或者用拼音首字母做前缀匹配——输入hz命中"杭州"这类,initial字段上的索引是能吃到的。
3.3 缓存策略与查询计划确认
行政区划数据是典型的读多写零,缓存收益极高。我通常的做法是在应用启动时把全量数据加载进本地缓存,三千条记录占内存不到 1MB,之后所有查询都走内存,数据库只作为持久化存储:
-- 只捞必要字段,减小缓存对象体积 SELECT code, name, short_name, parent_code, level, initial FROM sys_region WHERE status = 1;如果服务是多实例部署,本地缓存各自加载一份也没问题,数据量小、更新频率低,不需要 Redis 那一层。真有增量更新需求时,发个广播消息让各实例重载就行,比引入分布式缓存简单得多。
索引是否被正确使用,用执行计划确认一遍最稳妥:
EXPLAIN SELECT code, name FROM sys_region WHERE parent_code = '900000';type应该是ref,key应该是idx_parent,rows是几十这个量级。如果type显示ALL,说明索引没走,常见原因是parent_code的字符集和查询条件的字符集不一致,隐式转换导致索引失效。还有一种情况是查询条件写成了parent_code = 900000(数字),MySQL 会把字符串列转成数字比较,索引同样失效,这个坑很隐蔽,因为 SQL 不报错,只是慢。
4. 数据校验:怎么证明这份代码表没有缺漏、错位和过期
4.1 三条 SQL 把结构问题全捞出来
导入完成不等于数据可用,尤其是从各种渠道流转过来的 SQL 包,缺行、错位、父级指向不存在的记录都可能存在。下面三条 SQL 建议每次导入后都跑一遍:
-- 1. 父级不存在的孤儿节点 SELECT r.code, r.name, r.parent_code, r.level FROM sys_region r LEFT JOIN sys_region p ON r.parent_code = p.code WHERE r.level > 1 AND p.code IS NULL; -- 2. 代码重复 SELECT code, COUNT(*) AS cnt FROM sys_region GROUP BY code HAVING COUNT(*) > 1; -- 3. 名称重复但代码不同(同名不同层级,需要人工确认) SELECT name, COUNT(*) AS cnt, GROUP_CONCAT(code) AS codes FROM sys_region WHERE status = 1 GROUP BY name HAVING COUNT(*) > 1;第一条查孤儿节点,结果非空说明有记录的父级代码在表里找不到对应行,这种记录在三级联动里永远显示不出来,属于"藏起来的脏数据"。第二条靠唯一索引就能拦住,但如果索引是后加的,历史数据可能已经重复了。第三条不是错误,跨省同名县确实存在,但同一个省内出现同名同级单位就该人工看一眼了,大概率是数据源拼错了。
4.2 用编码规则反推层级是否自洽
六位编码自带校验能力,代码本身就能告诉你它该属于哪一层。用一条 SQL 把所有"代码形态和 level 字段对不上"的记录挑出来:
SELECT code, name, level, CASE WHEN SUBSTRING(code, 3, 4) = '0000' THEN 1 WHEN SUBSTRING(code, 5, 2) = '00' THEN 2 ELSE 3 END AS expected_level FROM sys_region WHERE level <> CASE WHEN SUBSTRING(code, 3, 4) = '0000' THEN 1 WHEN SUBSTRING(code, 5, 2) = '00' THEN 2 ELSE 3 END;expected_level完全由代码推导出来,和存储的level不一致就说明这条记录有问题。常见情况是数据源把省级单位标成了 level 2,或者把县级市标成了 level 2——县级市的代码后两位不是00,规则上就应该是 3。这种字段错位在按 level 分组统计时会让数字看着很奇怪,但对查询本身没影响,所以特别容易被忽略,等到做权限按层级过滤时才暴露。
再补一条父级层级校验,确认"地级的父级必须是省级":
SELECT c.code, c.name, c.level, p.code AS parent_code, p.level AS parent_level FROM sys_region c JOIN sys_region p ON c.parent_code = p.code WHERE c.level - p.level <> 1;正常情况下子级 level 永远比父级大 1。差值不等于 1 说明中间跳级了,典型的是县级单位直接挂在省级下面(省直辖县),父级 level 是 1、子级 level 是 3,差值是 2。这类记录不一定是错的,但必须心里有数,查询逻辑要能兜住。
4.3 新旧版本比对:增量到底变了什么
区划调整每年都有,拿到新版数据包之后,别急着整表覆盖,先做一次 diff 看看影响面:
import csv def load(path): with open(path, newline="", encoding="utf-8") as f: return {row["code"]: row for row in csv.DictReader(f)} old = load("region_old.csv") new = load("region_new.csv") added = new.keys() - old.keys() # 新增代码 removed = old.keys() - new.keys() # 撤销代码 renamed = [c for c in new.keys() & old.keys() if new[c]["name"] != old[c]["name"]] # 更名 print(f"新增 {len(added)} 条,撤销 {len(removed)} 条,更名 {len(renamed)} 条") for c in sorted(added)[:20]: print("+", c, new[c]["name"]) for c in sorted(removed)[:20]: print("-", c, old[c]["name"]) for c in sorted(renamed)[:20]: print("~", c, f"{old[c]['name']} -> {new[c]['name']}")load函数把 CSV 读成以 code 为键的字典,两个字典做集合运算就能拿到三类变更。只打印前 20 条是为了看趋势,真实场景里应该把完整清单落成一张region_change_log表,记录变更时间、变更类型、变更前后名称。有了这张表,历史订单里的旧代码在展示时可以查一张映射表还原成新名称,不用去猜。撤销的代码不要物理删除,把status置 0 就够了,删掉之后再想追溯就真的没有后悔药了。
5. 避坑排查:行政区划数据落地最容易翻车的五件事
5.1 编码与字符集层面的坑
现象:导入完成后name字段整列是问号,或者中文变成一堆看不懂的方块。
原因基本是两个:一是mysql客户端默认字符集不是utf8mb4,脚本里的 UTF-8 字节被按 latin1 解释后存进库;二是库或表的 collation 设成了latin1_swedish_ci,即使客户端对了,落盘时也会被转一次。这两种损坏都不可逆,改字符集不会把已有数据修好。
解决方式是在导入命令里显式带上--default-character-set=utf8mb4,并且提前确认SHOW CREATE DATABASE mydb输出的字符集是utf8mb4。已经导入坏了的表只能TRUNCATE之后重来,所以在导入前养成先建库、先查字符集的习惯,比事后修数据便宜得多。
现象:导出的 CSV 用 Excel 打开,省级代码01变成1,再存回去就丢了前导零。
这是 Excel 把纯数字列当成数值类型处理的经典行为,跟 SQL 无关。字段本身是CHAR(6),库里存的是对的,问题出在"导出-编辑-再导入"这条链路上。解决方式是导出时给代码列强制加文本格式,或者在 CSV 里用制表符前缀;如果一定要用 Excel 做人工核对,把该列格式先设成"文本"再粘贴数据,别直接双击打开。
5.2 层级结构层面的坑
现象:某直辖市的区县在三级联动里选不出来,市级下拉框是空的。
直辖市下面有一层"市辖区"作为伪地级,区县的真实parent_code指向的是这个伪地级,而不是省级代码。如果前端逻辑是"选中省之后按省级代码查子节点",查到的只有伪地级这一条,再往下就断了。
解决方式有两种:一是查询时把 level 的判断放宽,省级节点的子节点同时兼容parent_code = 省代码和parent_code IN (该省下所有市级代码);二是在数据层做一次归一化,插入时把直辖市的区县parent_code直接改写成省级代码,同时把city字段另存到一个city_code列里保留原始信息。我一般选第二种,SQL 里多写一次 UPDATE,前端逻辑就能保持统一,不用到处写特殊判断。
现象:省直辖县级单位成了孤儿,任何一级下拉框都找不到它。
这类单位的parent_code指向省级代码,但它自己的 level 是 3,中间那一层不存在。前端的"省-市-县"三段式联动天然容不下这种跨级挂载。解决办法是在构建树的时候做一次兜底:如果某个节点的父级 level 与自身 level 差值大于 1,就给它挂一个虚拟的"省直辖"节点作为父级,名称固定、代码用约定值,前端展示成"某省 / 省直辖 / 某某县"就行。这样做的好处是树的形状保持规整,树遍历、面包屑导航这些通用逻辑都不用改。
5.3 时效性层面的坑
现象:用户反馈说选到了一个前几年就已经撤县设区的名字,而新设的区在系统里根本找不到。
数据包是静态的,区划调整却是动态的,拿到手不更新,一年之后就开始出现偏差。判断数据新不新,看两处:文件命名里的年份标识,以及数据里最近一次区划调整是否包含在内。如果包本身没有版本号,可以挑几个近年调整过的区划代码去表里查一下,查不到就是旧版。
解决思路是把更新做成流程而不是一次性动作。表里保留status和updated_at,每次导入新版本前先跑 4.3 节的 diff 脚本生成变更清单,新增的插入、撤销的置失效、更名的更新名称,做到可追溯。撤销的代码永远不删,历史数据还能正常展示,只是在新业务里不再出现。
现象:多环境导入同一份脚本,测试库能查到三千条,线上库只查得到两千八。
先比对两边的SELECT COUNT(*),再比对SELECT MAX(code)和主键最大值。差异通常来自三处:导入过程中断但没报错(客户端连接超时被静默 kill)、脚本里带了WHERE条件的分批插入只跑了第一批、线上库有唯一索引导致部分 INSERT 被INSERT IGNORE吞掉。最后这种情况最容易忽略,因为导入过程没有任何报错提示,只是数据少了。稳妥的做法是导入前先DROP再CREATE,导入后强制跑一遍数量校验,把三条数字(省级、地级、县级)对齐了再切流量。
6. 二次开发:把静态代码表改造成可增量维护的区划服务
拿到 SQL 包直接用是第一步,真正省事的是包一层薄薄的接口。我一般用几十行代码起一个只读服务,SQLite 做后端就够了,三千条数据完全不需要上 MySQL:
from fastapi import FastAPI import sqlite3 app = FastAPI() def get_conn(): conn = sqlite3.connect("region.db") conn.row_factory = sqlite3.Row # 让结果支持按列名访问 return conn @app.get("/region/children") def children(parent: str = "000000"): """按父级代码查直属下级,parent 传 000000 返回所有省级""" with get_conn() as conn: rows = conn.execute( "SELECT code, name, short_name, level " "FROM sys_region " "WHERE parent_code = ? AND status = 1 " "ORDER BY code", (parent,), ).fetchall() return [dict(r) for r in rows]parent参数用查询串传递,默认值000000表示根节点。SQL 用参数化写法?占位,避免拼接字符串带来的注入风险,虽然这里只是查表,但习惯要统一。status = 1这个过滤必须带上,否则已撤销的区划会混进下拉框,用户选了之后提交到下游系统又是一轮排查。返回值里带short_name是为了前端在窄屏下显示简称,level则是给调用方判断要不要继续往下钻。
在这层接口之上可以做两个改造。第一个是缓存,服务启动时把全量数据读进字典,按parent_code建索引,后续请求直接查内存,响应时间从毫秒级降到微秒级,代价是改数据要重启。第二个是增量更新,单独建一张region_version表记录每次导入的版本号和数据截止时间,接口返回时带上version字段,前端可以据此判断本地缓存要不要失效。这两个改造都不复杂,但能让一份静态数据长期可用。
有个习惯我保持了几年:每次换新版本的 SQL 包,先把新旧两份导入两个临时库,跑一遍 diff 脚本,把新增、撤销、更名的条数记进变更表,确认没有异常大面积的变动之后,才更新正式库。有一次拿到的包少了整整一个地级单位下面的所有区县,数量校验直接报了红,要是不做这一步,线上就是几千条地址挂不上区县。从那以后我每次导入行政区划数据都强制走一遍"数量校验 + diff 比对 + 孤儿节点检查"这三步,一步都不敢省。希望帮到你。
本文还有配套的精品资源,点击获取