Rails 5.1(postgresql):高效的数据库查询,用于选择父记录和过滤的子记录

时间:2018-02-22 21:17:18

标签: sql postgresql optimization ruby-on-rails-5

我正在开发一个任务管理系统,其中project有多个taskstask可以分配到users

我想添加一个“我的任务”页面,其中显示当前用户任务下的所有项目。

像这样,每个任务都分配给current_user

Project #1
- task 1
- task 2
Project #2
- task 1
- task 2
Project #3
- task 1
- task 2
- task 3

我想用伪ActiveRecord代码实现的目标:

@projects_with_tasks = current_user.projects.includes(:tasks).where(tasks: { user_id: current_user.id })

然后我想迭代每个项目,列出分配给current_user的任务:

<% @projects.each do |project| %>
  <%= project.title %>
  <ul>
    <% project.tasks.each do |task| %>
      <li><%= task.title %> - <%= task.due_date %></li>
    <% end %>
  </ul>
<% end %>

看起来很简单,但是当我调用project.tasks时,它会返回并加载项目的所有任务,而不仅仅是current_user的任务。

有没有办法有效地获取项目和过滤的任务列表?

我目前最好的解决方案是首先获取所有项目,然后迭代它们并进行单独的数据库查询以检索所有已过滤的任务。但是,如果某人有20多个项目参与(可能在我的用例中),则会有21个以上的查询(1个用于所有项目,然后1个用于任务)。别介意一些用户将拥有50个项目......

我更喜欢将所有内容保存在ActiveRecord中,但我也知道这可能是创建一个带有一些SQL的查询对象的情况。

2 个答案:

答案 0 :(得分:0)

我假设您的has_many :tasks文件中有User.rb,而belongs_to :project

中有Task.rb

然后,您只需获取current_user.tasks.includes(:project),然后从那里获取唯一的项目列表。

答案 1 :(得分:0)

如果您的模型定义如下:

# app/models/user.rb
class User < ActiveRecord::Base
  has_many :tasks
end

# app/models/task.rb
class Task < ActiveRecord::Base
  belongs_to :project
  belongs_to :user
end

# app/models/project.rb
class Project < ActiveRecord::Base
  has_many :tasks
end

您可以使用此查询查询带有已过滤任务的项目:

@projects = Project.includes(:tasks).joins(:tasks).where(Task.table_name => { user_id: current_user.id })

.includes(:tasks) - 急切地加载任务

.joins(:tasks) - 执行INNER JOIN而不是LEFT OUTER JOIN

.where(Task.table_name => { user_id: current_user.id }) - 按用户过滤任务

您还可以为Task模型添加范围:

scope :for_user, ->(user) { where(user: user) }

之后,查询可以这样写:

@projects = Project.includes(:tasks).joins(:tasks).merge(Task.for_user(current_user))

输出结果相同。

注意:

我想你不会这样做,但是值得一提的是你应该避免在迭代加载了上述任何查询的项目时调用project.tasks.reload。它将强制重新加载tasks关联,而不会被用户过滤。