在Rails多表查询中避免N + 1查询

时间:2019-07-12 14:14:52

标签: ruby-on-rails postgresql active-model-serializers

这是我目前遇到的查询:

SELECT t1.discipline_id AS discipline1,
  t2.discipline_id AS discipline2,
  COUNT(DISTINCT t1.product_id) as product_count
FROM (SELECT "product_disciplines".* FROM "product_disciplines") t1
INNER JOIN (SELECT "product_disciplines".* FROM "product_disciplines") t2
ON t1.product_id = t2.product_id
WHERE (t1.discipline_id < t2.discipline_id)
GROUP BY t1.discipline_id, t2.discipline_id
ORDER BY "product_count" DESC

基本上,我有一个产品和学科列表,每个产品可能与一个或多个学科相关联。这个查询让我找出每个可能(不同)的学科对有多少产品与之相关。我将其用作dependency wheel in Highcharts的输入。

当我涉及Active Model Serializers时出现问题。这是我的控制器:

class StatsController < ApplicationController
  before_action :get_relationships, only: [:relationships]

  def relationships
    x = @relationships
      .select('t1.discipline_id AS discipline1, t2.discipline_id AS discipline2, COUNT(DISTINCT t1.product_id) as product_count')
      .order(product_count: :DESC)
      .group('t1.discipline_id, t2.discipline_id')
    render json: x, each_serializer: RelationshipSerializer
  end

  private

  def get_relationships
    query = ProductDiscipline.all

    @relationships = ProductDiscipline
      .from(query, :t1)
      .joins("INNER JOIN (#{query.to_sql}) t2 on t1.product_id = t2.product_id")
      .where('t1.discipline_id < t2.discipline_id')
  end
end

each_serializer指向此类:

class RelationshipSerializer < ApplicationSerializer
  has_many :disciplines do
    Discipline.where(id: [object.discipline1, object.discipline2])
  end
  attribute :product_count
end

查询数据库时,大约有1300个可能的对,这将在1300个学科查询中转换我的单个查询。

有没有办法避免这种结构的N + 1查询问题?

2 个答案:

答案 0 :(得分:0)

该RelationshipSerializer从哪个模型调用?我不明白为什么您只需要在Discipline模型中的一栏指出该模型时就需要使用object.disciplne1object.discipline2。在序列化程序中,您将使用:

has_many:disciplines,class_name:“ :: Discipline”

在调用此序列化程序之前,请确保您的activerecord查询对eager loading使用include(:disciplines)。

答案 1 :(得分:0)

我最终将其分为两个单独的API查询。 for i in sku_list.iterator(): print(i.sku) 仅保存学科ID,

RelationshipSerializer

因为在我的应用中,我已经需要可用学科列表,所以我选择将它们与客户端相关联。