仅查找其所有 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)')
我有一个 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)')