SQLAlchemy多关联下查询Flask事件规划应用用户可见活动的优化方法
Flask可见活动查询优化方案
原有实现的核心问题
- 重复结果:使用
union_all不会对结果去重,同一活动符合多个可见规则时会重复返回。如果改用union去重又会产生额外的性能开销。 - 好友数据遗漏:仅查询了好友表中
a_id为活动作者、b_id为当前用户的单向关系,漏掉了当前用户在a_id、好友在b_id的反向关系,会丢失一半好友发布的仅好友可见活动。 - 查询性能低下:多个子查询合并的执行方式需要数据库分别执行每条查询再合并结果集,无法利用索引做一次性过滤,数据量越大性能差距越明显。
优化后的实现方案
用OR条件拼接所有可见规则,单条SQL查询性能更高,也天然避免重复数据:
from sqlalchemy import or_, exists def visible_events(): # 未登录用户仅返回公开活动 if not current_user.is_authenticated: return Event.query.filter(Event.privacy == 4) # 判断活动作者是否为当前用户好友(覆盖双向好友关系) is_friend = exists().where( or_( (friendship.c.a_id == Event.user_id) & (friendship.c.b_id == current_user.id), (friendship.c.a_id == current_user.id) & (friendship.c.b_id == Event.user_id) ) ) # 判断当前用户是否已被邀请/已RSVP,有going、not_going表可在下方追加条件 is_invited = exists().where( (invited.c.event_id == Event.id) & (invited.c.user_id == current_user.id) # 示例:追加going表判断 | (going.c.event_id == Event.id) & (going.c.user_id == current_user.id) # 示例:追加not_going表判断 | (not_going.c.event_id == Event.id) & (not_going.c.user_id == current_user.id) ) # 单查询拼接所有可见条件,性能远高于多子查询union return Event.query.filter( or_( Event.privacy == 4, # 所有公开活动 Event.user_id == current_user.id, # 自己创建的活动 (Event.privacy == 3) & is_friend, # 好友发布的仅好友可见活动 (Event.privacy == 2) & is_invited # 已收到邀请/RSVP的仅邀请可见活动 ) )
这里用exists子查询替代多表join,仅做存在性判断,不需要拉取多余的好友、邀请关联数据,执行效率更高。
可选的底层优化建议
- 索引优化:给
events表的(privacy, user_id)字段创建联合索引,可进一步加快可见性过滤的速度。 - 关联表简化:
invited、going、not_going三个表结构完全一致,可以合并为一个用户活动关联表,新增status字段标识状态(1=邀请中、2=已参加、3=不参加),减少多表关联的开销。 - 双向好友关系优化:如果你的好友关系是需要双向确认的,可在加好友时同时写入两条记录(a->b和b->a),能简化好友判断逻辑,进一步提升查询效率。
内容的提问来源于stack exchange,提问作者Brandon
相关产品推荐
相关产品推荐

