架构:
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;
任何想法如何做到这一点?
答案 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
作为一种方法。 (可能有更好的名字。)