如何在Rails 4中通过查询简化这些组?

时间:2014-08-05 16:42:42

标签: sql ruby-on-rails

架构:

Word - 1:n - Training
WordList - 1:n - Word

我有一个单词列表的标题,并希望为它创建单词训练统计数据。

这些是我想到的两个SQL查询:

myId = SELECT word_list.id as id FROM word_lists WHERE title = 'mytitle'

SELECT words.*, count(trainings.word_id) as timesTrained
FROM words, trainings
WHERE
    words.word_list_id = myId
    trainings.word_id = words.id
GROUP BY trainings.word_id

在Rails中,这是我目前的做法。我怎样才能缩短它,所以需要两个查询而不是多个查询?

list = WordList.where(:title => params['wordlist'])
words = Word.where(:word_list => list)
for w in words
  w['timesTrained'] = Training.group(:word_id).where(:word_id => w.id).count()[w.id]
end

更新 我的第一个查询没有用,我需要的是以下查询(我检查过,在psql中工作),在Rails中尽可能简单:

SELECT words.*, p.ct
FROM words LEFT OUTER JOIN
    (SELECT trainings.word_id, count(trainings.word_id) as ct
     FROM trainings
     GROUP BY trainings.word_id) as p
ON words.id = p.word_id;

任何想法如何做到这一点?

1 个答案:

答案 0 :(得分:1)

第一个可以是:

my_id = WordList.where(title: params['wordList']).pluck(:id).first

第二个可以是:

words = Word.select('words.*, count(trainings.word_id) as times_trained').
joins("LEFT JOIN trainings on trainings.word_id = words.id").
where(word_list_id: my_id).
group("trainings.word_id")

(这是完全未经测试的,所以如果您遇到问题请告诉我,您可以解决这个问题,我会更新问题。)

访问您的值:

words.each { |w| puts "#{w.id}: #{w.times_trained}" }

第一个查询可以使用' find_by'如果您想要在找到多个相同名称的标题时抛出异常,这可能会更好。

编辑:

您在编辑中的进一步查询:

SELECT words.*, p.ct
FROM words LEFT OUTER JOIN
    (SELECT trainings.word_id, count(trainings.word_id) as ct
     FROM trainings
     GROUP BY trainings.word_id) as p
ON words.id = p.word_id;

使用ActiveRecord,我们强制使用字符串插值连接到子表。这些方面应该有用:

subquery = Training.select("trainings.word_id, count(trainings.word_id) as ct").
                    group(:word_id)

Word.select("words.*, p.ct").
     joins("LEFT OUTER JOIN (#{subquery.to_sql) as p ON words.id = p.word_id")

延迟.to_sql,直到你在字符串插值中需要它为止,意味着subquery可以用稍后的ActiveRecord / Arel / Squeel子句组成和修改。事实上,根本不使用局部变量是一个好主意,而是构建一个查询对象来封装你的行为并将subquery作为一种方法。 (可能有更好的名字。)