为何UNION ALL无法充分去重?SQL导入固定宽文件后去重异常
首先得澄清一个关键概念:UNION ALL根本不会做去重操作——它的作用只是把多个数据集的行全部合并在一起,不管有没有重复。你说的“仅能移除28条重复行”,大概率是你实际用的是UNION(它会自动去重,但只处理跨数据集的重复),而剩下的1200多条重复都藏在单个文件(也就是单个industryN表)的内部,UNION管不到这些。
针对你整行(所有28字段)完全重复的情况,给你几个实用的解决方案:
方案一:先单表去重,再合并
既然重复行可能存在于单个文件内部,那我们先对每个industryN表单独去重,再用UNION ALL合并(此时已经没有重复了,UNION ALL效率比UNION高很多):
SELECT DISTINCT * FROM industry1 UNION ALL SELECT DISTINCT * FROM industry2 -- 中间依次写完industry3到industry27 UNION ALL SELECT DISTINCT * FROM industry28
DISTINCT *会自动过滤每个表内的整行重复,再合并后就能彻底去掉那1257条重复。
方案二:先合并所有数据,再整体去重
如果觉得写28个SELECT DISTINCT太麻烦,可以先把所有数据合并到一个临时结果集,再用GROUP BY(或DISTINCT)整体去重:
-- 用GROUP BY的方式(对所有字段分组,自动保留唯一行) SELECT col1, col2, col3, ..., col28 -- 列出所有28个字段 FROM ( SELECT * FROM industry1 UNION ALL SELECT * FROM industry2 -- 中间依次写完industry3到industry27 UNION ALL SELECT * FROM industry28 ) AS combined_data GROUP BY col1, col2, col3, ..., col28; -- 或者更简洁的DISTINCT方式 SELECT DISTINCT * FROM ( SELECT * FROM industry1 UNION ALL SELECT * FROM industry2 -- 中间依次写完industry3到industry27 UNION ALL SELECT * FROM industry28 ) AS combined_data;
注意:不同数据库对GROUP BY的语法要求不同,比如PostgreSQL、SQL Server要求所有非聚合字段都要出现在GROUP BY里,刚好符合我们的需求;MySQL如果开启了ONLY_FULL_GROUP_BY也需要这么写,这反而更严谨。
方案三:利用复合主键约束,导入时自动去重
既然你提到有9个字段构成复合主键,而重复行是整行(包括主键)完全重复,那可以直接借助数据库的主键约束来自动跳过重复行,这是最彻底的方法:
- 先创建带复合主键的目标表:
CREATE TABLE industry ( -- 先定义你的28个字段,比如: pk_field1 INT, pk_field2 VARCHAR(30), -- ... 剩下7个主键字段 other_field1 VARCHAR(50), other_field2 DATE, -- ... 剩下19个非主键字段 -- 定义复合主键 PRIMARY KEY (pk_field1, pk_field2, ...) -- 列出那9个主键字段 );
- 然后逐个导入数据,让数据库自动忽略重复主键的行:
- MySQL用
INSERT IGNORE:
INSERT IGNORE INTO industry SELECT * FROM industry1; INSERT IGNORE INTO industry SELECT * FROM industry2; -- ... 依次导入到industry28
- PostgreSQL用
ON CONFLICT DO NOTHING:
INSERT INTO industry SELECT * FROM industry1 ON CONFLICT DO NOTHING; INSERT INTO industry SELECT * FROM industry2 ON CONFLICT DO NOTHING; -- ... 依次导入到industry28
- SQL Server用
MERGE:
MERGE INTO industry AS target USING (SELECT * FROM industry1) AS source ON target.pk_field1 = source.pk_field1 AND target.pk_field2 = source.pk_field2 -- ... 加上所有9个主键字段的匹配条件 WHEN NOT MATCHED THEN INSERT VALUES (source.*); -- 对每个industryN表重复上述MERGE语句
这个方法不仅能解决当前的重复问题,还能确保后续导入数据时不会再出现重复行,一劳永逸。
为什么之前只移除了28条重复?
大概率是那28条重复是跨文件的重复(比如industry1里的某一行和industry2里的某一行重复),而UNION只会移除这种跨数据集的重复;剩下的1257-28=1229条重复都在单个文件内部,UNION不会处理这些,所以才只去掉了28条。
内容的提问来源于stack exchange,提问作者Calflamesfann

