具有Ruby on Rails状态的关联数

问题描述 投票:0回答:2

我有一个名为Project的模型,而Project有许多任务

任务可以具有3个不同的状态(整数)。

我想获得状态为1、2和3的具有相关任务计数的项目列表。

我能得到的最好的是在Project上有一个方法

def open_tasks
  self.tasks.where(:status => 1).count
end

但是这将为每个计数生成另一个SQL,并且在加载100个项目时性能非常差。

是否有一种方法可以在一条SQL语句中实现?

ruby-on-rails rails-activerecord
2个回答
0
投票

我可以想到几种方法来做到这一点...

  1. (这不是单个sql语句,而是两个,尽管仍然很有效)...Task.where(status: 1).group(:project_id).count将为您提供一个哈希值,其中键是项目ID,值是任务计数。然后,您可以将其与项目列表结合在一起。

  2. 您可以使用ActiveRecord counter_cache在项目记录中保存打开任务数的值。 ActiveRecord将自动为您更新。我相信您需要像这样向项目模型添加关联:

# app/models/project.rb
# needs to include a column called open_task_count
class Project < ActiveRecord::Base
  has_many :open_tasks, class_name: Task, -> { where status: 1 }
end

class Task < ActiveRecord::Base
  belongs_to :project, counter_cache: true
end

0
投票
Project.select(
  'projects.*',
  '(SELECT COUNT(tasks.*) FROM tasks WHERE tasks.project_id = projects.id AND tasks.status = 0) AS status_0_count',
  '(SELECT COUNT(tasks.*) FROM tasks WHERE tasks.project_id = projects.id AND tasks.status = 1) AS status_1_count'
).left_joins(:tasks)

尽管有许多更优雅的方式(例如横向联接和CTE),但子查询在大多数DB上起作用。如果状态为ActiveRecord::Enum,则可以通过遍历枚举映射来构造子查询:

class Project < ApplicationRecord
  has_many :tasks
  def self.with_task_counts
    # constucts an array of SQL strings
    statuses = Task.statuses.map do |key, int|
      sql = Task.select('COUNT(*)')
             .where('tasks.project_id = projects.id')
             .where(status: key)
             .to_sql
      "(#{sql}) AS #{key}_tasks_count"
    end
    select(
      'projects.*',
      *statuses # * turns the array into a list of args
    ).left_joins(:tasks)
  end
end

在Rails 4中,您仍然可以通过使用SQL字符串进行LEFT OUTER JOIN:

class Project
  def self.left_joins_tasks(*args)
    deprecator = ActiveSupport::Deprecation.new("5.0", "MyApp")
    deprecator.deprecation_warning("left_joins_tasks is deprecated, use `.left_joins(:tasks)` instead")
    joins('LEFT OUTER JOIN tasks ON tasks.project_id = projects.id')
  end
end

使用.joins也可以,但是提供了INNER连接,因此可以过滤掉没有任务的行。您也可以使用.includes

© www.soinside.com 2019 - 2024. All rights reserved.