You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.31 00:54:40