如何在所有项目任务完成时自动更新项目状态?
高效实现项目状态自动切换:所有任务完成时标记项目为已完成
你的第一个方法确实存在严重的效率问题——循环每个项目并重复查询任务,属于典型的N+1查询问题,而且每次都执行update操作,不管状态是否需要变更,会产生大量不必要的数据库请求。下面我来帮你优化这个逻辑,用高效的批量查询和更新来实现需求。
核心思路:筛选"无未完成任务"的项目
要找到所有任务都已完成的项目,本质是找不存在任何状态为not_complete任务的项目。这里有两种高效的ActiveRecord查询方式:
方式1:使用exists子查询(推荐,可读性高)
通过子查询判断项目是否存在未完成任务,反向筛选出符合条件的项目:
# 筛选当前用户下,所有任务都已完成的项目 completed_projects = Project.where(user_id: current_user) .where.not( exists: Task.where('tasks.project_id = projects.id') .where(task_status: 'not_complete') )
方式2:使用left_joins + group + having
通过左连接任务表,分组统计未完成任务的数量,筛选数量为0的项目:
completed_projects = Project.left_joins(:tasks) .where(user_id: current_user) .group('projects.id') .having('COUNT(CASE WHEN tasks.task_status = ? THEN 1 END) = 0', 'not_complete')
注:如果需要排除没有任何任务的项目,可以在查询中添加
.where('tasks.id IS NOT NULL')
批量更新项目状态
得到符合条件的项目后,我们可以用update_all批量更新状态,避免循环单条更新:
# 1. 将所有任务完成的项目标记为complete Project.where(user_id: current_user) .where.not( exists: Task.where('tasks.project_id = projects.id') .where(task_status: 'not_complete') ) .update_all(project_status: 'complete') # 2. 将存在未完成任务的项目标记为not complete Project.where(user_id: current_user) .where( exists: Task.where('tasks.project_id = projects.id') .where(task_status: 'not_complete') ) .update_all(project_status: 'not complete')
为什么你之前的joins查询不对?
你写的Project.joins(:tasks).where(tasks: { task_status: false })是在筛选至少有一个任务状态为false的项目,和我们需要的"所有任务都完成"正好相反——这个查询得到的是需要标记为not_complete的项目,而不是completed的。
用上面的批量方法,整个逻辑只需要2次数据库查询+2次批量更新,相比原来的循环N次查询+N次更新,效率提升非常明显。
内容的提问来源于stack exchange,提问作者Kurak
相关产品推荐
相关产品推荐

