问题描述
我有一个Album
对象,其中有许多Tracks
。轨道上有一个youtube_uid
列。我想查询专辑中所有曲目的youtube_uids
所在的专辑。我知道这样一种技术,即找到带有曲目的专辑,其中至少有一个带有youtube_uid
的曲目:
Album.left_outer_joins(:tracks).where.not(tracks: { youtube_uid: nil })
查找每首曲目都带有youtube_uid
的专辑的理想查询是什么?
解决方法
据我所知,您想查找所有 not 曲目中缺少(空的)youtube-uid的专辑。所以afaik您需要一个NOT EXISTS
查询。
在sql中,我会写类似
select * from albums a
where not exists (select * from tracks where album_id = a.id and youtube_uid is null)
那么我们如何最好地将其转换为activerecord?我看到两种可能性:
sql = <<-SQL
select * from albums a
where not exists (select * from tracks where album_id = a.id and youtube_uid is null)
SQL
Album.find_by_sql(sql)
这可以正常工作,而且对于我来说,我很熟悉SQL,这感觉很好,不是很像“ rails-like”,所以我们可以改进它吗?
有一个简短的表格:
Album.where("not exists (select * from tracks where album_id = albums.id and youtube_uid is null")
但这仍然有点太冗长。 幸运的是,存在一种更像铁轨的方式。在rails 4中,您可以编写:
Album.where(Track.where("album_id = albums.id").where(youtube_uid: nil).exists.not)
在Rails 5/6中这不再可行,您必须编写:
Album.where(Track.where("album_id = albums.id").where(youtube_uid: nil).arel.exists.not)
您可以通过在末尾添加to_sql
来轻松验证这是否生成了良好的sql。
我们将与Group by并同时拥有:
Album.left_outer_joins(:tracks).group(:id).having('COUNT(tracks.id) = COUNT(tracks.youtube_uid)')