PostgreSQL双向用户屏蔽系统:如何实现正确的帖子可见性查询?
双向屏蔽机制下的帖子可见性SQL解决方案
你的问题核心是原查询用了INNER JOIN,导致用户没屏蔽任何人时查不到内容,而且只能拿到Block表有记录的用户帖子。咱们换个思路,先明确哪些用户的帖子是当前用户不能看的,然后排除这些,同时确保用户能看到自己的帖子,这样逻辑更清晰。
修正后的SQL语句
SELECT p.* FROM Post p WHERE p.user_id = @current_user_id OR p.user_id NOT IN ( -- 找出当前用户主动屏蔽的人 SELECT blocked_id FROM Block WHERE blocker_id = @current_user_id UNION -- 找出屏蔽了当前用户的人(双向屏蔽规则) SELECT blocker_id FROM Block WHERE blocked_id = @current_user_id );
逐行解释逻辑
- 优先显示自己的帖子:
p.user_id = @current_user_id这部分保证用户不管有没有屏蔽别人,总能看到自己发的内容,这是最基础的需求。 - 构建屏蔽用户列表:
- 第一个子查询拿到当前用户主动屏蔽的所有用户ID;
- 第二个子查询拿到所有屏蔽了当前用户的ID(因为是双向屏蔽,这些用户的内容也不能看);
- 用
UNION合并两个列表,去重后得到完整的“不可见用户”集合。
- 筛选可见帖子:
p.user_id NOT IN (...)确保只显示不在屏蔽列表里的用户的帖子。
验证你的预期场景
- 用户1(ID=1):屏蔽列表是用户2(主动屏蔽)+ 用户2(被屏蔽),排除用户2的帖子,最终看到自己和用户3的帖子,符合预期。
- 用户2(ID=2):屏蔽列表是用户1(主动屏蔽)+ 用户1(被屏蔽),排除用户1的帖子,看到自己和用户3的帖子,符合预期。
- 用户3(ID=3):Block表里没有涉及3的记录,所以屏蔽列表为空,
NOT IN条件对所有用户都成立,加上自己的帖子,就能看到所有内容,完美符合需求。
可选的LEFT JOIN写法
如果你对NOT IN的行为不太放心(其实PostgreSQL里空集合的NOT IN会返回所有结果,完全没问题),也可以用LEFT JOIN来实现相同逻辑,可读性可能更好:
SELECT p.* FROM Post p -- 检查帖子作者是否被当前用户屏蔽 LEFT JOIN Block b1 ON p.user_id = b1.blocked_id AND b1.blocker_id = @current_user_id -- 检查帖子作者是否屏蔽了当前用户 LEFT JOIN Block b2 ON p.user_id = b2.blocker_id AND b2.blocked_id = @current_user_id WHERE p.user_id = @current_user_id -- 两个关联都没匹配上,说明没有双向屏蔽关系 OR (b1.id IS NULL AND b2.id IS NULL);
内容的提问来源于stack exchange,提问作者ccoeder




