SQL 5.7.37中如何扩展UNION语句合并5个及以上同结构同主键的表?
针对你用SQL 5.7.37合并多表且基于Title(唯一索引)去重的需求,我整理了几种和你原有逻辑兼容且扩展性强的方案,帮你轻松处理5个及以上表的合并:
方案1:延续你原有的递进式NOT EXISTS逻辑
这个方案完全沿用你之前双表合并的思路,逐个添加后续表中未出现过的Title行,确保先出现的表中的数据被优先保留(比如Table1的Chad会被保留,Table2/4的Chad会被过滤)。以合并Table1到Table4为例,语句如下:
CREATE TABLE table5 AS SELECT * FROM table1 UNION ALL -- 只取Table2中Title不在Table1里的行 SELECT * FROM table2 WHERE NOT EXISTS (SELECT 1 FROM table1 WHERE table1.Title = table2.Title) UNION ALL -- 只取Table3中Title不在Table1和Table2里的行 SELECT * FROM table3 WHERE NOT EXISTS (SELECT 1 FROM table1 WHERE table1.Title = table3.Title) AND NOT EXISTS (SELECT 1 FROM table2 WHERE table2.Title = table3.Title) UNION ALL -- 只取Table4中Title不在前三个表里的行 SELECT * FROM table4 WHERE NOT EXISTS (SELECT 1 FROM table1 WHERE table1.Title = table4.Title) AND NOT EXISTS (SELECT 1 FROM table2 WHERE table2.Title = table4.Title) AND NOT EXISTS (SELECT 1 FROM table3 WHERE table3.Title = table4.Title);
如果要加第5个表,只需要在后面追加一段类似的UNION ALL,检查前面所有表的Title即可。这个逻辑很直观,和你原来的代码风格一致,SQL 5.7完全支持。
方案2:用UNION自动去重(仅限整行完全重复的场景)
如果你的场景中,相同Title对应的所有列内容都完全一致(比如Table2和Table4里的Chris行完全一样),那可以直接用UNION代替UNION ALL,因为UNION会自动去掉整行重复的记录:
CREATE TABLE table5 AS SELECT * FROM table1 UNION SELECT * FROM table2 UNION SELECT * FROM table3 UNION SELECT * FROM table4;
⚠️ 注意:UNION是基于所有列去重的,如果存在Title相同但DESC或URL不同的行,UNION会保留这些行,这不符合你的需求。所以这个方案只适合相同Title的行完全一致的情况。
方案3:窗口函数去重(扩展性最好的方案)
SQL 5.7.22及以上版本支持窗口函数,这个方案更简洁,不管多少个表,只需要在内部的UNION ALL里追加表即可。核心思路是先把所有表的行合并,然后为每个Title标记行号,只保留每个Title的第一行:
CREATE TABLE table5 AS SELECT ID, Title, `DESC`, URL FROM ( SELECT *, -- 按Title分组,给每个组的行标记序号 ROW_NUMBER() OVER (PARTITION BY Title ORDER BY (SELECT 0)) AS rn FROM ( SELECT * FROM table1 UNION ALL SELECT * FROM table2 UNION ALL SELECT * FROM table3 UNION ALL SELECT * FROM table4 ) AS all_merged_tables ) AS ranked_tables -- 只保留每个Title的第一行 WHERE rn = 1;
这里的ORDER BY (SELECT 0)是让数据库随机选每个Title的一行,如果想优先保留更早的表中的行(比如优先Table1,然后Table2),可以修改排序逻辑,给每个表加个优先级标记:
CREATE TABLE table5 AS SELECT ID, Title, `DESC`, URL FROM ( SELECT *, ROW_NUMBER() OVER (PARTITION BY Title ORDER BY table_priority) AS rn FROM ( SELECT *, 1 AS table_priority FROM table1 UNION ALL SELECT *, 2 AS table_priority FROM table2 UNION ALL SELECT *, 3 AS table_priority FROM table3 UNION ALL SELECT *, 4 AS table_priority FROM table4 ) AS all_merged_tables ) AS ranked_tables WHERE rn = 1;
这样每个Title会优先保留优先级数字小的表中的行,完全匹配你原来的逻辑。
额外提醒:关于自增ID的问题
你的原表中ID是自增主键,但合并后的Table5里的ID可能会重复(比如Table1和Table2都有ID=1)。如果需要Table5的ID也是自增唯一的,建议创建表时重新定义ID:
CREATE TABLE table5 ( ID INT AUTO_INCREMENT PRIMARY KEY, Title VARCHAR(255) UNIQUE, `DESC` TEXT, URL VARCHAR(255) ) AS -- 这里只选需要的字段,不选原表的ID,让新表自动生成 SELECT Title, `DESC`, URL FROM (...); -- 这里放上面任意一种方案的子查询
这样新表的ID会从1开始自动递增,不会有重复问题。
内容的提问来源于stack exchange,提问作者Mae Webb

