SQL多表合并查询使用distinct仍存在重复记录问题排查
问题原因
你写的关联查询出现无法用distinct消除的重复,核心原因有两个:
- 关联逻辑存在维度交叉:
PLAYERS到PLAYERS_SERVICE是一对多关系(单个玩家可对应多条服务记录),PLAYERS到PLAYERS_PROPERTY也是一对多关系(单个玩家可对应多条属性记录),两个关系是平行独立的,直接链式join会产生笛卡尔积——比如某玩家有2条服务记录、3条属性记录,关联后会生成2*3=6条记录,这类记录因为服务字段、属性字段取值不同,属于逻辑上不同的行,distinct按整行全字段比对,自然无法去重。 - 原三个独立查询是三组独立的关联校验规则,你强行把所有表字段拼到同一行的写法,本身就不符合原查询的逻辑诉求。
解决方案
根据你实际的结果诉求,选对应写法即可:
方案1:需要筛选同时满足三个关联规则的玩家,取单粒度玩家数据
用EXISTS做过滤,不会产生任何重复行,查询性能也更优:
SELECT p.* FROM PLAYERS p -- 匹配「玩家-服务记录-数据记录」关联规则 WHERE EXISTS ( SELECT 1 FROM PLAYERS_SERVICE ps JOIN PLAYERS_DATA pd ON pd.ID = ps.ID WHERE ps.PLAYER_ID = p.PLAYER_ID ) -- 匹配「玩家-属性记录」关联规则 AND EXISTS ( SELECT 1 FROM PLAYERS_PROPERTY pp WHERE pp.PLAYER_ID = p.PLAYER_ID )
如果需要同时取另外两张表的单值字段(比如最新登录时间、VIP等级这类聚合值),先对一对多表做按玩家ID聚合,保证每个玩家只对应一行后再关联,从根源避免笛卡尔积:
SELECT p.*, pd_stat.last_login_time, pp_stat.vip_level FROM PLAYERS p JOIN ( SELECT ps.PLAYER_ID, MAX(pd.last_login_time) AS last_login_time FROM PLAYERS_SERVICE ps JOIN PLAYERS_DATA pd ON pd.ID = ps.ID GROUP BY ps.PLAYER_ID ) pd_stat ON pd_stat.PLAYER_ID = p.PLAYER_ID JOIN ( SELECT pp.PLAYER_ID, MAX(pp.vip_level) AS vip_level FROM PLAYERS_PROPERTY pp GROUP BY pp.PLAYER_ID ) pp_stat ON pp_stat.PLAYER_ID = p.PLAYER_ID
方案2:需要合并三个原查询的所有结果(不需要跨表拼字段)
用UNION ALL拼接三个查询的结果,注意对齐三个查询的字段数量、字段类型即可:
-- 第一个查询:PLAYERS_DATA 关联 PLAYERS_SERVICE SELECT 'data_service' AS data_source, pd.*, ps.*, NULL AS player_field_placeholder, NULL AS property_field_placeholder FROM PLAYERS_DATA pd, PLAYERS_SERVICE ps WHERE pd.ID = ps.ID UNION ALL -- 第二个查询:PLAYERS 关联 PLAYERS_SERVICE SELECT 'player_service' AS data_source, NULL AS data_field_placeholder, ps.*, p.*, NULL AS property_field_placeholder FROM PLAYERS p, PLAYERS_SERVICE ps WHERE p.PLAYER_ID = ps.PLAYER_ID UNION ALL -- 第三个查询:PLAYERS_PROPERTY 关联 PLAYERS SELECT 'property_player' AS data_source, NULL AS data_field_placeholder, NULL AS service_field_placeholder, p.*, pp.* FROM PLAYERS_PROPERTY pp, PLAYERS p WHERE pp.PLAYER_ID = p.PLAYER_ID
方案3:确实需要所有关联表的全量明细
这种场景下出现的多行列是正常结果——因为单个玩家对应多条服务、多条属性,交叉后必然生成多行记录,此时不要用distinct强行去重,否则会误删合法的明细数据,重复字段的冗余展示可以放到业务代码层处理。
内容的提问来源于stack exchange,提问作者Neel
相关产品推荐
相关产品推荐

