在Rails中具有多个关系的分组查询

时间:2017-10-09 16:35:08

标签: sql ruby-on-rails activerecord rails-activerecord

我使用分类模型在Projets和Categories之间建立了belongs_to关系。 Projet class Client < ApplicationRecord has_many :projets end class Category < ApplicationRecord has_many :categorizations, dependent: :destroy has_many :projets, through: :categorizations end class Categorization < ApplicationRecord belongs_to :category belongs_to :projet end class Projet < ApplicationRecord belongs_to :client has_many :categorizations, dependent: :destroy has_many :categories, through: :categorizations end 客户。

def listing
  @projets_clients = Projet
    .select("client_id")
    .includes(:client)
    .joins(:categorizations)
    .where(categorizations: { category: @category })
    .group("client_id")

  @clients = []
  @projets_clients.each do |p|
    @clients << Client.includes(:projets).find(p.client_id)
  end
end

对于特定类别,我想列出所有按客户分组的项目。 e.g。

(对于category_id = 3)

客户A. Projet 1 Projet 2

客户B Projet 3

客户C. Projet 4

到目前为止,我可以使用它,但只能使用两个查询(其中一个查询非常低效(n + 1问题)。

这是代码

var $actions = application.service('$actions', [
function() {
    let _logout = function() {
        $('#logoutForm').submit();
    }
    return {
        **Logout() {**
            _logout();
        },
    };
}]);

如果有人能提出更好的方法,我很乐意学习如何优化这一点,因为我自己找不到更好的方法。

感谢。

1 个答案:

答案 0 :(得分:1)

有几种不同的方法可以做到这一点。对于复杂查询,我有时会发现编写和执行直接SQL更容易。但是对于您的情况,根据数据大小,您可以急切地加载数据并将其转换为哈希值。

注意:当我测试此代码时,我使用的是projects而不是projets

@category = Category.includes(projects: [:client]).find(2)
@projects_by_client = @category.projects.group_by(&:client_id)

# In your view
<%- @projects_by_client.each do |client_id, projects| %>
  <%= projects.first.client.name %>
  <%- projects.each do |project| %>
    <%= project.name %>
  <% end %>
<% end %>

更加丰富的解决方案可能会将完整的sql与查询对象和演示者对象一起使用。我使用下面的代码创建了一个快速项目,输出就是你要找的。

# app/controllers/clients_controller.rb
class ClientsController < ApplicationController
  def show
    result = ClientQuery.call(params[:id])
    @presenter = ClientPresenter.new(result)
  end
end

# app/services/client_query.rb
class ClientQuery
  class << self
    def call(client_id)
      sql_query(client_id)
    end

    protected

    def sql_query(client_id)
      ActiveRecord::Base.
        connection.
        execute(
          sanitized_sql_statement(client_id)
        )
    end

    def sanitized_sql_statement(client_id)
      ActiveRecord::Base.send(
        :sanitize_sql_array,
        [
          sql_statement,
          client_id
        ]
      )
    end

    def sql_statement
      <<-SQL
        SELECT
          c.id AS client_id,
          c.name AS client_name,
          p.name AS project_name
        FROM
          clients c
        INNER JOIN
          projects p ON p.client_id = c.id
        INNER JOIN
          categorizations cz ON cz.project_id = p.id
        INNER JOIN
          categories ct ON ct.id = cz.category_id
        WHERE
          ct.id = ?;
      SQL
    end
  end
end

# app/presenters/client_presenter.rb
class ClientPresenter
  attr_reader :clients

  def initialize(data)
    @clients = {}
    process_sql_result(data)
  end

  private

  def process_sql_result(data)
    data.each do |row|
      client_id = row['client_id']

      @clients[client_id] ||= { client_name: row['client_name'] }
      @clients[client_id][:projects] ||= []
      @clients[client_id][:projects] << row['project_name']
    end
  end
end


# app/views/show.html.erb
<%- @presenter.clients.each do |client_id, client_presenter| %>
  <h1><%= client_presenter[:client_name] %></h1>
  <ul>
  <%- client_presenter[:projects].each do |project_name| %>
    <li><%= project_name %></li>
  <% end %>
  </ul>
<% end %>

这当然只是您在单个查询中呈现数据并展示数据的众多方法之一。