Search code examples
ruby-on-railsactiverecordsubquery

Rails ActiveRecord subquery on Inner Join


I am struggling to use Rails' ActiveRecord query interface to replicate a query that has an inner join subquery. How would I replicate the following:

SELECT ass.name, COUNT(DISTINCT a.question_id) AS 
  answered_questions, tq.total_questions
  FROM assessments AS ass
  INNER JOIN  (SELECT ass.id, COUNT(q.id) AS total_questions FROM                         
    questions AS q INNER JOIN assessments AS ass ON ass.id=q.assessment_id 
    GROUP BY 
    ass.id) as tq ON tq.id=ass.id
  INNER JOIN questions AS q ON q.assessment_id=ass.id
  INNER JOIN answers AS a ON a.assessment_id=ass.id AND a.question_id=q.id
  INNER JOIN org_assesments AS oa ON ass.id=oa.assessment_id
  INNER JOIN users AS u ON oa.org_id=u.org_id AND       
    a.user_id=u.id
  WHERE  u.id=1
  GROUP BY ass.name, tq.total_questions
  ORDER BY ass.created_at DESC
  LIMIT 10

I don't seem to be able to get this to work with the subquery using the query builder. Without the subquery I have this, which works and gives me the assessment title and number of questions answered:

Question.joins(:assessment => {:org_assessments => {:org => :users}}).joins(:answers)
                 .where(answers:{:user_id => params[:id]})
                 .distinct('answers.question_id').group(['assessments.name']).count()

How can I write this to include the subquery as in the original SQL above?


Solution

  • You may send the subquery as a string to the joins method:

    sub_query =
      TotalQuestion.
        joins(:assessments).
        group('assessments.id').
        select('assessments.id, COUNT(q.id) as total_questions').to_sql
    
    Question.joins("(#{sub_query}) as tq on tq.id=ass.id")
    

    And you can combine it with the other parts of the query:

    Question.
        joins(:assessment => {:org_assessments => {:org => :users}}).joins(:answers).
        joins("(#{sub_query}) as tq on tq.id=ass.id").
        where(answers:{:user_id => params[:id]}).
        distinct('answers.question_id').group(['assessments.name']).count()