仅在其所有has_many集合均具有特定列值的情况下查找对象

问题描述

我有一个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)')