简介:这份数据包面向需要处理行业维度数据清洗与标准化的大数据技术人员,完整汇集了2002、2011、2017三个年度发布的国民经济行业分类国家标准(GB/T 4754-2002、GB/T 4754-2011、GB/T 4754-2017),并统一为“门类·大类·中类·小类”的四级结构。每个行业代码都按这一结构拆分,并提供跨版本的汇总统计,例如A0111对应“农、林、牧、渔业·农业·谷物及其他作物的种植·谷物的种植”,可直接核对不同版本间行业代码的变化,降低手工整理国标的时间成本。压缩包共3个文件,均为SQL格式,分别存放三个年份的建表与数据,总大小仅44KB,适合导入MySQL数据库,快速用于数据治理、数据仓库建模或统计分析。压缩包内文件组织清晰,便于单独导入或对比使用。资源已有3038人学习或下载,尤其适合需要建立历史行业口径映射表、解决不同版本分类冲突的数据分析与开发人员。
1. 国民经济行业分类与代码:三年版本MySQL打包,行业数据清洗的后悔药
做行业维度数据清洗的人大概率都被“年份魔咒”折磨过:业务库里的行业代码是2008年的,可最新标准已经换到2017版;销售表里存着旧门类代码,财务系统却按新代码出报表,两套对齐时只能靠人工翻标准。这份资源把2002、2011、2017三个版本的国民经济行业分类与代码做成了MySQL数据文件,每一行都是带完整四级路径的代码和名称。
国民经济行业分类采用“门类·大类·中类·小类”四级结构,代码规则在三个版本中各有差异,直接跨年比对必然翻车。资源内的std_code_2002.sql、std_code_2011.sql、std_code_2017.sql三个文件表结构一致,查询和清洗逻辑可以复用,适合数据仓库建模、指标口径统一、跨年度对比分析的技术人员直接导入使用。
2. GB/T 4754 标准演进:从2002到2017,四级分类体系怎么变
在动手导入SQL之前,先花点时间搞清三个版本之间的关系。行业分类代码不是随便编的,它是按照门类(字母A-T)、大类(两位数)、中类(三位数)、小类(四位数)逐级展开的。比如A0111这个代码,拆开看就是门类A(农、林、牧、渔业)、大类01(农业)、中类011(谷物及其他作物的种植)、小类0111(谷物的种植)。四条路径合起来就是摘要里那句“农、林、牧、渔业·农业·谷物及其他作物的种植·谷物的种植”。
版本差异的核心不是编码规则变了,而是类目本身在变。2002版、2011版、2017版分别对应GB/T 4754-2002、GB/T 4754-2011、GB/T 4754-2017,每一次标准更新都会新增一些当时的新兴行业,同时合并或删除一些过时的小类。如果你手里的数据跨越多个年份,做行业维度标准化之前,必须先把标准版本差异理清,否则映射工作会很痛苦。
2.1 门类·大类·中类·小类:四位编码怎么拆
国民经济行业分类的编码规则是“一个字母加四位数字”。字母代表门类,从A到T一共20个门类,覆盖农、林、牧、渔业,采矿业,制造业,电力、热力、燃气及水生产和供应业,建筑业,批发和零售业,交通运输、仓储和邮政业,住宿和餐饮业,信息传输、软件和信息技术服务业,金融业,房地产业,租赁和商务服务业,科学研究和技术服务业,水利、环境和公共设施管理业,居民服务、修理和其他服务业,教育,卫生和社会工作,文化、体育和娱乐业,公共管理、社会保障和社会组织,以及国际组织。四位数字中的前两位是大类代码,前三位是中类代码,四位全取是小类代码。
举个例子,C门类是制造业,C13是农副食品加工业(大类),C131是谷物磨制(中类),C1311是稻谷磨制(小类)。在表里,C1311这个代码对应的名称就是“制造业·农副食品加工业·谷物磨制·稻谷磨制”。名称的层级关系用“·”分隔,每一段分别对应一个层级。
关键点在于:表中的“代码”列存的是完整的“字母+四位数字”,而“名称”列存的是用“·”拼接的完整层级路径。两者是对应的,但查询时不能只靠名称列模糊匹配,应该优先用代码列做精确计算。因为名称在三个版本之间可能有微调,代码如果没变,关联关系就还在,名称变化不影响JOIN结果。
2.2 2002到2011:产业结构调整带来的类目增删
2002版是入世后的第一版行业分类标准,整体框架沿袭了之前的四级体系,但编排上更贴近当时的统计口径。2011版发布时,服务业占比已经明显上升,于是新增了“金属制品、机械和设备修理业”这个大类,同时把“开采辅助活动”从采矿业里单独拆出,放在B门类下作为大类存在。类似这种“大类级别”的变动,在代码映射时最麻烦,因为旧版里没有对应的整段代码范围。
具体到小类层面,2011版删掉了一些产能过剩相关的类目,比如部分矿产开采的小类被合并进相邻代码;新增了诸如“其他电信服务”“互联网信息服务”等细分项。如果业务数据产生于2005年左右,用2002版代码入库,后来要转成2011版口径,就必须逐条核对这类变动。
2.3 2011到2017:合并与拆分的关键变动
2017版(GB/T 4754-2017)是当前最常用的标准。这一版在2011版基础上新增了不少“互联网+”相关的行业类别,比如“互联网零售”“互联网生活服务平台”“互联网游戏服务”这些小类,反映出数字经济在国民经济统计里的权重。同时,一些传统制造业类目做了细化拆分,比如“汽车零部件及配件制造”在2011版下只有一个中类,2017版则按功能进一步分成几个小类。
另一个值得注意的变动是部分类目的“归属调整”。例如原属于某些门类的辅助性活动,在新版里被整体平移到了“科学研究和技术服务业”或者“租赁和商务服务业”。这类调整不会被写进小类名称里,如果你只比对代码和名称,很容易漏掉,必须对照官方修订说明才能确认。
所以做映射时我一般不会只看三个SQL文件,还会额外找一下官方发布的修订对照表。SQL文件提供的是最终代码和名称,修订对照表才详细说明每个代码变动的原因和对应关系。资源里虽然没有附带修订说明,但你可以用三版代码表交叉比对,先找出代码完全相同、代码不同但名称相似、代码和名称都完全不同的三类记录,再针对后两类做人工核对。
3. 数据表结构与SQL文件:std_code_2002.sql 怎么用
压缩包解压后是三个SQL文件:std_code_2002.sql、std_code_2011.sql、std_code_2017.sql。文件命名很直白,年份后缀对应的就是标准的发布年份。三个文件内部结构一致,都是两列:代码列和名称列。代码列存储“字母+四位数字”的完整编码,名称列存储“·”连接的完整四级名称路径。我先按2017版文件举例,2002和2011的用法完全一样,只是数据内容不同。
3.1 表结构解析:代码列与名称列的对应关系
导入SQL后,表里每一行的形态是这样的:
| 代码 | 名称 |
|---|---|
| A0111 | 农、林、牧、渔业·农业·谷物及其他作物的种植·谷物的种植 |
| C1311 | 制造业·农副食品加工业·谷物磨制·稻谷磨制 |
注意这里有个隐含信息:每一行存的都是小类级别。所谓四级结构,是指名称里包含四个层级的名称,而不是表里有四列。所以如果你想做“门类=大类”的粒度分析,不能直接在这张表里GROUP BY,必须先对代码做字符串处理,拆出对应的层,再单独建一张维度表。
我一般会在导入后立刻建一张拆分视图,把每个小类代码的父级代码段都算出来。SQL如下:
CREATE OR REPLACE VIEW v_industry_2017 AS SELECT 代码, 名称, SUBSTRING(代码, 1, 1) AS 门类代码, SUBSTRING(代码, 2, 2) AS 大类代码, SUBSTRING(代码, 2, 3) AS 中类代码, SUBSTRING(代码, 2, 4) AS 小类代码 FROM std_code_2017;逻辑说明:SUBSTRING(代码, 2, 2) 从第2个字符起取两位,拿到的就是大类代码。因为第1位是门类字母,从第2位开始才是数字段。中类代码取三位,小类代码取四位。门类代码单独截取第一位。视图建好之后,每一次查询都不用重复写SUBSTRING逻辑。
参数说明:如果你的代码列不是字母开头而是纯数字,SUBSTRING的起始位置要从2改成1,不要直接复制这段SQL。另外,SUBSTRING截取的结果是字符串类型,如果你需要跟业务表里的数字字段JOIN,记得用CAST转换,比如CAST(SUBSTRING(代码, 2, 2) AS UNSIGNED)。
3.2 导入MySQL:命令行三步走
第一步,建库。如果目标库还不存在,先建一个专门放维度表的库:
mysql -uroot -p -e "CREATE DATABASE IF NOT EXISTS dim DEFAULT CHARSET utf8mb4;"第二步,导入SQL文件。以2017版为例:
mysql -uroot -p dim --default-character-set=utf8mb4 < std_code_2017.sql这里把std_code_2017.sql导入到dim库。文件名如果和表名不一致也没关系,MySQL导入的是文件里的CREATE TABLE语句和INSERT语句,文件名只影响你在命令行里敲的路径。
第三步,验证行数:
SELECT COUNT(*) AS 小类总数 FROM std_code_2017; SELECT COUNT(*) AS 中类去重 FROM ( SELECT DISTINCT SUBSTRING(代码, 2, 3) FROM std_code_2017 ) t;第一步里的-e参数表示执行完命令就退出,适合在自动化脚本里用。第二步如果你用的是高版本MySQL客户端(8.0+),可能会遇到认证插件兼容问题,那个不在资源范围内,但可以换用source命令从mysql客户端内部导入:source /path/to/std_code_2017.sql;
3.3 常用查询:按层级筛选业务数据
表导入好后,最常见的用法是关联业务表,把存的行业代码翻译成名称。如果业务表里存的是A0111这种完整代码,直接JOIN即可:
SELECT b.订单号, s.名称 FROM 业务表 b LEFT JOIN std_code_2017 s ON b.行业代码 = s.代码;如果业务表里只存四位数字0111,没有门类字母,那要先把两边对齐:
SELECT b.订单号, s.名称 FROM 业务表 b LEFT JOIN std_code_2017 s ON CONCAT('A', b.行业代码) = s.代码;注意这里的CONCAT只能在你知道所有代码都属于同一个门类时才能用。实际业务中,业务表里的四位数字往往省略的是当前默认门类的字母,如果门类不确定,需要先反查行业名称来确认。这里就是很多人掉坑的地方,第五章会展开说。
3.4 文件的字符集兼容性
解压后的SQL文件如果直接双击打开,在Windows记事本里可能看到中文乱码,那不是文件坏了,是记事本默认按ANSI解析内容。导入MySQL时只要指定了--default-character-set=utf8mb4,乱码问题不会出现。如果你是在Linux环境用vim查看,需要用:set fileencoding=utf-8重新加载。
关于表的命名:三个SQL文件导入后会生成三张独立的表:std_code_2002、std_code_2011、std_code_2017。它们彼此不关联,字段结构相同。如果你需要跨年比对,可以之后把它们UNION ALL到一起,或者建一个带年份字段的汇总表。第4章会给出更完整的做法。
4. 三年数据对比:把业务代码映射到统一口径
资源的核心价值在于三年的代码表都有了,剩下的问题是“怎么用”。大多数数据仓库项目的行业维度标准化,都是把业务表中杂七杂八的行业代码统一到最新口径(2017版),并给每一行打上门类、大类、中类、小类的标签。下面我按实际项目里最常见的流程来拆解。
4.1 建一张带年份的汇总字典表
首先把三个版本的代码合成一张表,加一个年份字段,这样后续映射筛选才方便:
CREATE TABLE dim_industry_all AS SELECT '2002' AS 版本, 代码, 名称 FROM std_code_2002 UNION ALL SELECT '2011' AS 版本, 代码, 名称 FROM std_code_2011 UNION ALL SELECT '2017' AS 版本, 代码, 名称 FROM std_code_2017;这条SQL把三张表纵向堆到一起。UNION ALL保留重复行,不需要去重,因为同一个代码在不同版本里可能对应不同的名称,去重反而会丢数据。加完索引之后查询效率会更好:
ALTER TABLE dim_industry_all ADD INDEX idx_code (代码);逻辑说明:版本字段用字符串而不是数字,是因为2002、2011、2017三个值不是等差数列,字符串的辨识度更高。代码字段建索引,是为了后续按代码JOIN业务表时能用上索引,避免全表扫描。
参数说明:如果你希望查询结果更易读,可以把版本字段改成“GB/T 4754-2002”这种带标准号的写法,比如用CASE WHEN替换。但这会影响后续JOIN的SQL长度,建议保留简短值。
4.2 跨年映射:一对多、多对一的处理原则
现在回到最核心的问题:业务表里存的是2011版代码,怎么映射到2017版?
第一步,找出两个版本中都存在的代码:
SELECT a.代码, a.名称 AS 名称_2011, b.名称 AS 名称_2017 FROM std_code_2011 a LEFT JOIN std_code_2017 b ON a.代码 = b.代码 WHERE b.代码 IS NOT NULL;如果名称相同,说明这个代码没变,直接沿用。如果名称不同,说明代码虽然没变但叫法改了,还是可以直接沿用代码,但名称要用2017版替换。
第二步,找出只有旧版才有、新版没有的代码:
SELECT a.代码, a.名称 AS 名称_2011 FROM std_code_2011 a LEFT JOIN std_code_2017 b ON a.代码 = b.代码 WHERE b.代码 IS NULL;对这些代码,不能简单抛弃,而是要根据业务去判断:它在新版里是被合并了,还是被拆分了,还是真的被删除了。实际操作中,我一般会先用名称相似度做初筛:
SELECT a.代码 AS 旧代码, a.名称 AS 旧名称, b.代码 AS 新代码, b.名称 AS 新名称 FROM std_code_2011 a LEFT JOIN std_code_2017 b ON REPLACE(a.名称, '·', '') LIKE CONCAT('%', SUBSTRING_INDEX(b.名称, '·', 3), '%');这条SQL把旧名称里的“·”去掉,拿新名称的前三段做模糊匹配。注意LIKE两侧的字符串比较耗时,只适合几万行以内的小表,如果代码量很大,建议把名称拆列后做索引再匹配。
一对多和多对一没有标准答案。常见的做法是:如果一个旧代码对应多个新代码,按业务体量选一个主映射,其他映射写成辅助记录;如果多个旧代码对应一个新代码,直接统一到新代码。映射表结构建议做成这样:
CREATE TABLE dim_industry_mapping ( 旧版本 VARCHAR(4), 旧代码 VARCHAR(5), 新代码 VARCHAR(5), 映射类型 ENUM('不变','改名','合并','拆分','新增'), 备注 VARCHAR(255) );映射类型字段很有用。以后做指标同比环比时,如果发现某个行业的数据出现异常波动,可以先看映射类型,判断是代码变动引起的还是业务真实变动。
4.3 做行业标签标准化的完整流程
整理完映射关系后,在业务表上打标签的流程是这样的:
第一步,给业务表加几个冗余字段:
ALTER TABLE 业务表 ADD COLUMN 门类代码 VARCHAR(1), ADD COLUMN 大类代码 VARCHAR(2), ADD COLUMN 中类代码 VARCHAR(3), ADD COLUMN 小类代码 VARCHAR(4), ADD COLUMN 行业名称_标准 VARCHAR(200);第二步,用映射关系更新业务表:
UPDATE 业务表 b LEFT JOIN dim_industry_mapping m ON b.行业代码 = m.旧代码 AND m.旧版本 = '2011' LEFT JOIN std_code_2017 s ON m.新代码 = s.代码 SET b.门类代码 = SUBSTRING(s.代码, 1, 1), b.大类代码 = SUBSTRING(s.代码, 2, 2), b.中类代码 = SUBSTRING(s.代码, 2, 3), b.小类代码 = SUBSTRING(s.代码, 2, 4), b.行业名称_标准 = s.名称 WHERE b.数据年份 BETWEEN 2012 AND 2017;这里的UPDATE语句会把业务表的2011版代码先映射到2017版。如果你的业务表数据跨度超过十年,建议先统一到2011版再映射到2017版,分两步走,避免跳过中间版本导致映射偏差。
第三步,验证数据质量,看看有没有仍然没关联上的代码:
SELECT b.行业代码, b.数据年份, COUNT(*) FROM 业务表 b LEFT JOIN dim_industry_mapping m ON b.行业代码 = m.旧代码 WHERE m.旧代码 IS NULL GROUP BY b.行业代码, b.数据年份;如果这个查询有结果,说明业务表里存在三个版本标准以外的代码,很可能已经是2017版无需映射,也可能是录入错误。把结果导出,逐条人工确认。
5. 避坑与常见问题:导入失败、编码错乱、代码缺失的排错清单
接下来这部分全是实战中踩过的坑,按“现象→原因→解决”的方式整理。第3章里那几步看起来简单,真跑起来你会发现坑一个接一个。
5.1 SQL文件导入报“Unknown database”
现象:mysql < std_code_2017.sql 命令执行后,终端报错 ERROR 1049 (42000): Unknown database。
原因:这条命令隐含的语义是“把SQL文件应用到当前连接的默认数据库”,但如果你没在命令里指定库名,而当前连接又没有默认库,MySQL不知道把表建到哪里。大部分图形化客户端会自动选中当前操作的库,所以平时没遇到这个问题,一旦换命令行就翻车。
解决:导入命令里显式指定库名,并且先确保库已存在:
mysql -uroot -p dim --default-character-set=utf8mb4 < std_code_2017.sql5.2 导入后中文全部变成问号
现象:执行SELECT查询,名称列显示一堆“???”,比如“农、林、牧、渔业”变成“?????????”。
原因:SQL文件里的中文以UTF8编码存储,但导入时客户端和服务器之间的连接字符集不是UTF8,导致中文字节被当成单字节字符解析。还有一种可能是表结构里字符集被建成了latin1,而不是utf8mb4。
解决:导入时增加--default-character-set=utf8mb4,同时确认目标表字符集是utf8mb4:
SHOW CREATE TABLE std_code_2017;如果建表语句里没有DEFAULT CHARSET=utf8mb4,用下面的语句转换:
ALTER TABLE std_code_2017 CONVERT TO CHARACTER SET utf8mb4;5.3 三个表行数差异巨大,怀疑文件是不是缺数据
现象:std_code_2002.sql导入后只有一千多行,std_code_2017.sql有两千多行,有人会觉得2002版数据不全。
原因:这是标准本身的变化,不是文件问题。2002版的小类数量本来就少于2017版。每一版新增行业的数量与当时的统计覆盖范围相关,比如2002版里没有“互联网零售”这类类目,自然少一行。
解决:不要用行数多少判断文件完整性。正确的验证方式是抽几个已知代码出来查:比如A0111在三个版本里都存在,查出来都有就是对的;再抽几个2017版新增的行业代码,在2002版里查不到属于正常。
5.4 业务表里的四位代码JOIN不上
现象:业务表里存的是“0111”这种四位数字,代码表里存的是“A0111”,两边直接JOIN结果为空,或者错位。
原因:行业分类标准的完整代码必须是“字母+四位数字”,业务表里存的是省略门类字母的简写。如果业务表本身没有额外字段记录门类,你无法确定“0111”到底属于哪个门类——有可能是A0111,也可能是其他门类下的0111,比如C0111。
解决:不要盲目用CONCAT('A', 代码)去JOIN。先看业务表里有没有门类字段,如果有就用完整代码JOIN;如果没有,先统计一下业务表里出现的四位代码在哪个门类下唯一存在:
SELECT SUBSTRING(代码, 2, 4) AS 四位代码, COUNT(DISTINCT SUBSTRING(代码, 1, 1)) AS 门类数 FROM std_code_2017 GROUP BY SUBSTRING(代码, 2, 4) HAVING 门类数 > 1;如果门类数大于1的代码很少,可以对这些代码单独人工确认门类;如果很多,说明业务表的存储约定就是“默认门类+四位代码”,你只能找业务方要门类对照说明。
5.5 名称里的间隔符号不一致导致匹配失败
现象:用名称列做相似度匹配时,明明两个版本的名称看着一模一样,但REPLACE或LIKE就是匹配不上。
原因:肉眼看到的间隔符可能是同一个中点符号,但有的文件里用的是中文全角符号“·”(U+00B7),有的是西文句号,甚至可能是两个不同Unicode码位的点。SQL文件统一后一般没有这个问题,但如果你从其他渠道拿过补充数据,很容易混入不同的符号。
解决:做名称清洗时,先把所有间隔符统一成一种:
UPDATE 业务表 SET 行业名称 = REPLACE(REPLACE(REPLACE(行业名称, '·', '|'), '.', '|'), '、', '|');把中文的点、西文的点、顿号都替换成管道符,再做匹配。这个操作要在副本表上做,避免污染原始数据。
6. 进阶用法:把三版数据做成常驻映射视图,半年省下两天清洗时间
如果这个行业代码表只是临时用一次,前面的内容已经够用。但如果你所在的数据仓库每个季度都要做行业维度统计,我建议把三版代码做成常驻视图,顺便把映射关系固化下来。半年之后回头再看,这个初始搭建成本会帮你省掉大把手工对码时间。
6.1 建一张长期维护的行业维度宽表
宽表的结构建议包含:标准代码、标准名称、门类名称、大类名称、中类名称、小类名称。这样查询时不用每次都做字符串截取:
CREATE TABLE dim_industry_2017 AS SELECT s.代码 AS 标准代码, s.名称 AS 标准名称, SUBSTRING_INDEX(s.名称, '·', 1) AS 门类名称, SUBSTRING_INDEX(SUBSTRING_INDEX(s.名称, '·', 2), '·', -1) AS 大类名称, SUBSTRING_INDEX(SUBSTRING_INDEX(s.名称, '·', 3), '·', -1) AS 中类名称, SUBSTRING_INDEX(SUBSTRING_INDEX(s.名称, '·', 4), '·', -1) AS 小类名称 FROM std_code_2017 s;逻辑说明:SUBSTRING_INDEX(字符串, 分隔符, n) 取第n个分隔符之前的部分,再配合-1取倒数第一个,就能逐层拆出名称的每一段。这种方法比SUBSTRING按位置截取更稳,因为门类名称的字节长度不固定,比如“农、林、牧、渔业”是7个字符,而“制造业”只有3个字符,按位置截取容易错位。
参数说明:如果你要拆2011版或2002版,只需要把FROM后面的表名换成std_code_2011或std_code_2002,其他逻辑不变。这就是三个表结构一致带来的好处。
6.2 每日增量任务里加一个自动校验步骤
宽表建好之后,把第5章里的“未匹配代码查询”做成定时任务,每天扫描业务表里新增的行业代码,发现陌生代码就自动写入一张待人工确认表:
INSERT INTO dim_industry_unknown (行业代码, 数据年份, 出现次数, 发现时间) SELECT b.行业代码, b.数据年份, COUNT(*), NOW() FROM 业务表 b LEFT JOIN dim_industry_2017 s ON b.行业代码 = s.标准代码 WHERE s.标准代码 IS NULL GROUP BY b.行业代码, b.数据年份 ON DUPLICATE KEY UPDATE 出现次数 = VALUES(出现次数);这个INSERT会不断累积新的未知代码,运维人员只需要每周看一次待确认表,把新的映射关系补进dim_industry_mapping,而不是每次发现问题都临时翻标准。从那以后,我每次搭建行业维度数据都会先建好这张校验表,再开始业务清洗,省下的手工对码时间远超过建表那半小时。希望这篇文章的整理过程能帮到你,少走我当年踩过的那些坑。
本文还有配套的精品资源,点击获取