【问题标题】:is there a more efficient way to code this with ActiveRecord?有没有更有效的方法来使用 ActiveRecord 进行编码?
【发布时间】:2012-03-07 04:39:06
【问题描述】:

我有三张桌子:

  1. 用户
  2. 问题
  3. 用户问题

    1.user questions有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  

【问题讨论】:

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


    【解决方案1】:

    在模型类上调用all 方法几乎没有充分的理由。这会将数据库中该类型的每条记录加载到内存中,除非您绝对确定这是一小组记录,否则您可能会挂起整个系统。即使这样,加载所有内容然后挑选一件事并丢弃其余的也是非常糟糕的形式。这就像从亚马逊订购每一件商品,挑选出你想要的笔,然后将剩余的货物扔进垃圾桶。

    您可能想要的是随机选择 一个 尚未分配的记录。这可能看起来像这样:

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

    JOIN 的问题在于您将在user_questions 表中找到匹配的记录,而不是相反的记录。

    此查询假定用户回答的问题数量相对较少,或者NOT IN 的费用可能会大大增加。

    【讨论】:

    • 不同版本的 SQL 似乎需要不同版本的 'RANDOM()',但确实如此。
    【解决方案2】:

    是的,您可以为此使用 LEFT OUTER JOIN。普通的 INNER JOIN 只会包含匹配连接条件的行,LEFT JOIN 将包含匹配的行,并通过在所有列中放置 NULL 来充实不匹配的行(PostgreSQL docs 有一个合理的描述)。

    因此,您执行 LEFT JOIN,然后通过查找 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 中进行随机选择应该足够快,并且不会让你的数据库讨厌你。

    还可以查看documentation 以了解整理joins 的方法,我正在使用具有略微非标准结构的数据库,因此有时我必须做很长的路要走; ActiveRecord 拒绝为我执行 LEFT JOIN,除非我把它拼出来,YMMV。

    【讨论】:

      猜你喜欢
      • 2021-09-28
      • 1970-01-01
      • 2011-12-15
      • 1970-01-01
      • 2019-10-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多