仅查找其所有 has_many 集合都具有特定列值的对象

Finding objects only where all of their has_many collections have a value for a specific column

我有一个 Album 对象,其中有很多 Tracks。曲目有一个 youtube_uid 列。我想查询所有曲目的 youtube_uids 都存在的专辑。我知道查找专辑的技术,其中至少有一个曲目带有 youtube_uid:

Album.left_outer_joins(:tracks).where.not(tracks: { youtube_uid: nil })

要查找每首曲目都有 youtube_uid 的专辑,理想的查询是什么?

据我了解你的问题,你想找到所有 没有 的专辑都有缺失(空)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-like 的方法。在 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 和 having 来实现它:

Album.left_outer_joins(:tracks).group(:id).having('COUNT(tracks.id) = COUNT(tracks.youtube_uid)')