MySQL合并同结构表并按ID去重:优先保留用户自定义值
实现优先保留用户自定义数据的表合并
这个需求在多表数据合并、优先级覆盖的场景里很常见,我给你分享几个实用的SQL写法,都能精准满足你的需求:
方案一:UNION ALL + 排除已存在的ID(最直观高效)
这个思路特别简单:先取出所有用户自定义的table_saved数据,再补充table_default中那些没有被自定义覆盖的ID数据。这样既保证了自定义数据优先,又自动完成了去重。
-- 先取所有用户保存的自定义数据 SELECT ID, SOME_VAL FROM table_saved UNION ALL -- 再取默认表中未被自定义覆盖的部分 SELECT d.ID, d.SOME_VAL FROM table_default d WHERE NOT EXISTS ( SELECT 1 FROM table_saved s WHERE s.ID = d.ID );
执行这个语句后,你会得到完全符合预期的结果:
| ID | SOME_VAL |
|---|---|
| 1 | test |
| 3 | text |
| 2 | def_val |
这个方案的优势是逻辑清晰,性能也出色——只要给table_saved.ID建了索引,NOT EXISTS的查询效率会非常高。
方案二:使用窗口函数(灵活扩展)
如果以后你可能需要增加更多优先级的表(比如还有一个临时保存数据的table_temp),用窗口函数的写法会更灵活。我们可以给不同表的数据标记优先级,然后取每个ID下优先级最高的记录:
WITH combined_data AS ( -- 给saved表标记最高优先级(1),default表标记次优先级(2) SELECT ID, SOME_VAL, 1 AS priority FROM table_saved UNION ALL SELECT ID, SOME_VAL, 2 AS priority FROM table_default ) -- 每个ID只保留优先级最小的那条(也就是saved的数据优先) SELECT ID, SOME_VAL FROM combined_data WHERE (ID, priority) IN ( SELECT ID, MIN(priority) FROM combined_data GROUP BY ID );
或者用ROW_NUMBER()分区的写法,逻辑会更直观:
WITH combined_data AS ( SELECT ID, SOME_VAL, -- 按ID分组,给saved的记录排第1位,default排第2位 ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY CASE WHEN source = 'saved' THEN 1 ELSE 2 END ) AS rn FROM ( SELECT ID, SOME_VAL, 'saved' AS source FROM table_saved UNION ALL SELECT ID, SOME_VAL, 'default' AS source FROM table_default ) t ) -- 只取每个ID下排名第1的记录 SELECT ID, SOME_VAL FROM combined_data WHERE rn = 1;
这种写法的好处是扩展性极强,以后加新表只需要调整ORDER BY里的优先级规则就行。
方案三:LEFT JOIN + COALESCE(简洁但有局限)
我们也可以用左连接把两个表关联起来,然后用COALESCE函数优先取saved表的值,没有的话就用default表的:
SELECT d.ID, -- 如果saved表有对应ID的取值就用它,否则用default的 COALESCE(s.SOME_VAL, d.SOME_VAL) AS SOME_VAL FROM table_default d LEFT JOIN table_saved s ON d.ID = s.ID;
不过这个方案有个小局限:如果table_saved里存在table_default没有的ID(比如用户新增了一个默认表没有的自定义配置),这条数据会被漏掉。如果你的场景不存在这种情况,这个写法是最简洁的;如果存在,那需要再加上UNION ALL补全这部分数据:
SELECT d.ID, COALESCE(s.SOME_VAL, d.SOME_VAL) AS SOME_VAL FROM table_default d LEFT JOIN table_saved s ON d.ID = s.ID UNION ALL -- 补充saved表中存在但default表没有的ID SELECT ID, SOME_VAL FROM table_saved s WHERE NOT EXISTS ( SELECT 1 FROM table_default d WHERE d.ID = s.ID );
这样就和方案一的效果完全一致了。
内容的提问来源于stack exchange,提问作者Pavel K
相关产品推荐
相关产品推荐

