多表查询方案:筛选无游玩记录、合规统计值的非封禁玩家
最优SQL查询方案:筛选符合条件的玩家
首先我先明确你的需求细节,确保理解到位:
- 从
player表中筛选玩家,需同时满足:- 从未进行过游戏:该玩家在
player_log表中无任何活动记录(默认游戏行为会被写入player_log) - 统计值不超指定阈值:若玩家在
player_stat有记录,其stat值必须≤设定阈值;无player_stat记录的玩家也符合条件(毕竟从未玩过游戏可能没有统计数据) - 排除特定用户:排除已被封禁1天的用户
$uid(默认同时排除所有封禁1天的玩家,若你只想排除$uid这个特定账号,可随时调整条件)
- 从未进行过游戏:该玩家在
最优查询语句
假设:
player.status字段中,'ban_1d'代表“封禁1天”的状态(你可替换为实际状态值,比如数值1)- 阈值用
@threshold表示(替换为实际数值,比如100) - 各关联字段(
player.id、player_log.srl、player_stat.srl)已建立索引
SELECT p.* FROM player p -- 排除封禁1天的玩家及指定$uid WHERE p.status != 'ban_1d' AND p.id != $uid -- 从未进行过游戏:player_log中无该玩家记录 AND NOT EXISTS ( SELECT 1 FROM player_log pl WHERE pl.srl = p.id ) -- 统计值符合要求:要么无stat记录,要么stat≤阈值 AND ( NOT EXISTS ( SELECT 1 FROM player_stat ps WHERE ps.srl = p.id ) OR EXISTS ( SELECT 1 FROM player_stat ps WHERE ps.srl = p.id AND ps.stat <= @threshold ) );
为什么这是最优方案?
用
NOT EXISTS替代LEFT JOIN ... IS NULL:
数据库优化器对NOT EXISTS的处理效率更高,它找到第一条匹配记录后就会停止查询,避免全表扫描。尤其是当关联字段有索引时,性能提升会非常明显。逻辑拆分更清晰:
把“无stat记录”和“stat≤阈值”拆分为OR条件,既符合需求逻辑,又利用了子查询的短路特性,减少不必要的计算。避免结果集膨胀:
如果用LEFT JOIN关联player_stat,若一个玩家有多条stat记录,会产生重复的玩家数据;而EXISTS子查询只判断存在性,不会导致结果集冗余。
可选调整(根据实际需求)
- 若仅需排除
$uid特定用户(不管其是否封禁),可去掉p.status != 'ban_1d'条件,只保留p.id != $uid。 - 若要求玩家必须有stat记录且≤阈值,可删除
NOT EXISTS (...)部分,只保留EXISTS (SELECT 1 FROM player_stat ps WHERE ps.srl = p.id AND ps.stat <= @threshold)。 - 若要进一步优化性能,建议给
player_stat建立联合索引(srl, stat),这样判断stat≤阈值时可直接使用索引,无需回表查询。
内容的提问来源于stack exchange,提问作者hamid
相关产品推荐
相关产品推荐

