多列SQL表去重合并问题及MySQL索引1070报错求助
嘿,我完全懂你的困扰——列数多到爆炸的时候,不管是写NOT EXISTS的匹配条件还是创建唯一索引都要写疯,还撞上了MySQL的32列索引硬限制。下面给你几个接地气的解决思路,一步步来:
一、用哈希列绕过32列唯一索引限制
这是解决1070错误最直接的方法:把所有需要判断重复的非datetime列,拼接成字符串后计算哈希值,用这个哈希值做唯一索引——这样只需要一个索引列,直接避开32列的限制。
步骤1:给表添加哈希计算列
先给目标表新增一个存储哈希值的生成列(MySQL 5.7+支持),它会自动计算所有非datetime列的哈希:
ALTER TABLE audi_all ADD COLUMN hash_cols CHAR(32) AS ( MD5(CONCAT( COALESCE(Vehicle, 'NULL'), -- 处理NULL值,避免拼接后整个结果变成NULL COALESCE(`listed Price`, 'NULL'), COALESCE(Anunciante, 'NULL'), -- 继续把所有非datetime的列都加进来,字段名有空格记得加反引号 COALESCE(your_other_column, 'NULL'), COALESCE(your_last_column, 'NULL') )) ) STORED;
小提醒:如果有数字类型的列,最好用
CAST转成字符串(比如CAST(listed PriceAS CHAR)),避免不同格式的数字拼接后哈希不一致;要是对碰撞概率要求极高,可以把MD5换成SHA256,对应列类型改成CHAR(64)。
步骤2:创建唯一索引
现在只需要给哈希列创建唯一索引就行:
CREATE UNIQUE INDEX unq_audi_hash ON audi_all(hash_cols);
以后插入数据时,只要非datetime列完全相同,哈希值就会重复,唯一索引会自动拦截重复插入。
二、批量去重插入的简化写法
如果不想依赖唯一索引,直接做去重插入,也可以用哈希对比代替逐列写匹配条件:
INSERT INTO TABLE1 SELECT DISTINCT * FROM TABLE2 A WHERE NOT EXISTS ( SELECT 1 FROM TABLE1 X WHERE MD5(CONCAT( COALESCE(A.Vehicle, 'NULL'), COALESCE(A.`listed Price`, 'NULL'), -- 所有非datetime列和上面保持一致 COALESCE(A.your_last_column, 'NULL') )) = MD5(CONCAT( COALESCE(X.Vehicle, 'NULL'), COALESCE(X.`listed Price`, 'NULL'), COALESCE(X.your_last_column, 'NULL') )) );
这样不用逐列写A.col = X.col,代码瞬间清爽很多。
三、合并表的终极方案:临时表去重
如果是要一次性合并两个表并去重,可以先把所有数据导入临时表,再分组去重后插入目标表:
-- 1. 创建和目标表结构一致的临时表 CREATE TEMPORARY TABLE temp_audi LIKE audi_all; -- 2. 把两个表的数据都导进去 INSERT INTO temp_audi SELECT * FROM TABLE1; INSERT INTO temp_audi SELECT * FROM TABLE2; -- 3. 去重后插入目标表(如果目标表已有数据,可跳过TRUNCATE改用NOT EXISTS逻辑) TRUNCATE TABLE audi_all; INSERT INTO audi_all SELECT Vehicle, `listed Price`, Anunciante, ..., MAX(datetime_column) AS datetime_column FROM temp_audi GROUP BY Vehicle, `listed Price`, Anunciante, ... -- 所有非datetime列
这里用MAX(datetime_column)是为了保留每组中最新的时间记录,你也可以换成MIN或者其他逻辑,完全按需调整。
为什么会出现1070错误?
MySQL的InnoDB引擎对唯一索引的键部分数量有硬限制——最多32个,这是引擎底层的限制,没法直接突破,所以用哈希列的方法就是最实用的绕道路径。
内容的提问来源于stack exchange,提问作者Sanardi

