SQL四表连接问题:关联后出现重复数据如何解决?
嘿,我来帮你搞定这个重复记录的问题~从你的查询语句和描述来看,出现重复行的核心原因是:pickWire或pickInserting表中,同一个id对应了多条重复记录,当你用JOIN关联时,这些重复记录会和主表pickMaster的行产生笛卡尔积,最终导致输出结果里出现重复的行。
下面给你几种针对性的解决方案,你可以根据自己的业务场景选择:
1. 先排查并清理脏数据
如果业务上pickWire和pickInserting每个id应该只有一条记录,那大概率是数据脏了。你可以先执行以下查询,找出这两张表中重复的id:
-- 检查pickWire中重复的id SELECT id, COUNT(*) AS record_count FROM pickWire GROUP BY id HAVING COUNT(*) > 1; -- 检查pickInserting中重复的id SELECT id, COUNT(*) AS record_count FROM pickInserting GROUP BY id HAVING COUNT(*) > 1;
如果查出了重复的id,你可以根据业务规则删除多余的重复行(比如保留最新的一条),之后再执行原来的查询就不会有重复了。
2. 在查询中直接去重
如果业务允许pickWire/pickInserting一个id有多条记录,但你只需要每个id对应一条结果,那可以用以下几种方式处理:
方法一:使用DISTINCT快速去重
如果主表pickMaster的pick_id是唯一标识,且重复行的所有字段都完全一致,可以直接在SELECT后加DISTINCT。另外注意把原来的JOIN改成LEFT JOIN,这样更符合你用CASE处理NULL的逻辑(避免主表记录被过滤):
SELECT DISTINCT a.pick_id, a.Serial_No, a.Work_Ord_No, a.Lot_No, a.Product_no, a.Plan_Qty, a.Machine_no, a.shift, a.Scan_dt, b.Trml_code, CASE WHEN c.Wire_Type IS NULL THEN '-' ELSE CONCAT(c.Wire_Type, ' ', c.Wire_Size, ' ', c.Wire_Color) END AS Wire, CASE WHEN d.Mtrl_code IS NULL THEN '-' ELSE d.Mtrl_code END AS Material FROM pickMaster a JOIN pickTerminal b ON b.id = a.id LEFT JOIN pickWire c ON c.id = a.id LEFT JOIN pickInserting d ON d.id = a.id;
方法二:用窗口函数提取唯一记录
如果同一个id下的记录有差异,你需要指定规则取某一条(比如最新的、最早的),可以用窗口函数ROW_NUMBER():
WITH UniqueWire AS ( SELECT id, Wire_Type, Wire_Size, Wire_Color, -- 按你需要的规则排序,比如按创建时间取最新的 ROW_NUMBER() OVER (PARTITION BY id ORDER BY create_dt DESC) AS rn FROM pickWire ), UniqueMaterial AS ( SELECT id, Mtrl_code, ROW_NUMBER() OVER (PARTITION BY id ORDER BY create_dt DESC) AS rn FROM pickInserting ) SELECT a.pick_id, a.Serial_No, a.Work_Ord_No, a.Lot_No, a.Product_no, a.Plan_Qty, a.Machine_no, a.shift, a.Scan_dt, b.Trml_code, CASE WHEN c.Wire_Type IS NULL THEN '-' ELSE CONCAT(c.Wire_Type, ' ', c.Wire_Size, ' ', c.Wire_Color) END AS Wire, CASE WHEN d.Mtrl_code IS NULL THEN '-' ELSE d.Mtrl_code END AS Material FROM pickMaster a JOIN pickTerminal b ON b.id = a.id LEFT JOIN UniqueWire c ON c.id = a.id AND c.rn = 1 LEFT JOIN UniqueMaterial d ON d.id = a.id AND d.rn = 1;
如果没有时间字段,也可以用ORDER BY (SELECT NULL)随机取一条,但建议尽量用有业务意义的排序字段。
方法三:用聚合函数合并重复记录
如果同一个id下的Wire或Material字段值其实是相同的,只是重复存储了,可以用MAX()/MIN()这类聚合函数,配合GROUP BY来合并:
SELECT a.pick_id, a.Serial_No, a.Work_Ord_No, a.Lot_No, a.Product_no, a.Plan_Qty, a.Machine_no, a.shift, a.Scan_dt, b.Trml_code, COALESCE(CONCAT(MAX(c.Wire_Type), ' ', MAX(c.Wire_Size), ' ', MAX(c.Wire_Color)), '-') AS Wire, COALESCE(MAX(d.Mtrl_code), '-') AS Material FROM pickMaster a JOIN pickTerminal b ON b.id = a.id LEFT JOIN pickWire c ON c.id = a.id LEFT JOIN pickInserting d ON d.id = a.id GROUP BY a.pick_id, a.Serial_No, a.Work_Ord_No, a.Lot_No, a.Product_no, a.Plan_Qty, a.Machine_no, a.shift, a.Scan_dt, b.Trml_code;
总结
优先排查数据是否存在不符合业务规则的重复记录,这是从根源解决问题的方式;如果业务本身允许重复存在,再根据实际需求选择去重方法。
内容的提问来源于stack exchange,提问作者Amarul Samsudin

