Есть ли более эффективный способ закодировать это с ActiveRecord? - PullRequest
3 голосов
/ 07 марта 2012

У меня есть три таблицы:

  1. Пользователи
  2. Вопросы * * 1006
  3. User_Questions

    1. Вопросы пользователей содержат столбцы user_id, question_id и answer

Я хочу найти случайный вопрос, на который не был дан ответ и, следовательно, не имеет строки в таблице user_questions .

Если на каждый вопрос дан ответ, верните любой случайный вопрос.

Мне сказали, что это можно сделать с помощью OUTER JOIN, но я нуб SQL и не уверен, как это сделать в Rails.
Вот что у меня есть:

def next_question          
  q = Question.all - Question.joins(:user_questions).where
       (user_questions: { user_id: user_id })
  q = Question.all if q.empty?
  return q[rand(q.size)]
end  

Ответы [ 2 ]

1 голос
/ 07 марта 2012

Да, вы можете использовать левое внешнее соединение для этого.Обычное INNER JOIN будет включать только строки, соответствующие условию соединения, LEFT JOIN будет включать в себя совпадающие строки и выделит несопоставленные строки, поместив NULL во все столбцы ( Документы PostgreSQL имеют разумное описание).

Итак, вы делаете ЛЕВОЕ СОЕДИНЕНИЕ, а затем ищите несопоставленную строку, ища NULL.SQL будет выглядеть примерно так:

select ...
from questions
left outer join user_questions on questions.id = user_questions.question_id
where user_questions.question_id is null

Это даст вам все вопросы, на которые еще нет ответа.В ActiveRecord вы можете сделать что-то вроде этого:

Question.joins('left outer join user_questions on question.id = user_questions.question_id')
        .where(:user_questions => { :question_id => nil })
        .limit(1)

Возможно, вы захотите поиграть и со случайным порядком, но это должно помочь вам начать.Если это не дает вам соответствия, тогда вы можете получить случайный вопрос с чем-то вроде этого:

n = Questions.count
q = Questions.offset(rand(n)).limit(1)

Вы можете сделать то же самое с Questions.order('random()').limit(1), но ORDER BY random() может быть неприятно сбольшая база данных;получение счетчика должно быть довольно быстрым, поэтому случайный выбор в Ruby должен быть достаточно быстрым, и ваша база данных не будет вас ненавидеть.

Также ознакомьтесь с документами .Чтобы привести это в порядок, joins, я работаю с базой данных с немного нестандартной структурой, поэтому иногда приходится делать что-то долгое;ActiveRecord отказывается делать ЛЕВОЕ СОЕДИНЕНИЕ для меня, если я не произнесу это, YMMV.

1 голос
/ 07 марта 2012

Вряд ли есть веская причина для вызова метода all в классе модели.Это загружает каждую запись в базе данных этого типа в память, и если вы не уверены, что это небольшой набор записей, вы можете потенциально повесить всю свою систему.Даже тогда очень плохо загружать в все , а затем Черри выбирает одну вещь, а остальные отбрасывает.Это все равно, что заказать один из каждого товара в Amazon, выбрать нужную ручку и выбросить оставшуюся доставку в корзину.1007 * запись, которая еще не была назначена.Вероятно, это выглядит примерно так:

Question.where('id NOT IN (SELECT question_id FROM user_questions WHERE user_id=?)', user_id).order('RAND()').first

Проблема с JOIN состоит в том, что вы найдете записи, которые имеют совпадения в таблице user_questions, а не в обратном.

В этом запросе предполагается, что количество вопросов, на которые ответил пользователь, относительно мало или что NOT IN может значительно дороже.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...