从ActiveRecord has_many关联中获取唯一值

时间:2016-01-31 09:17:46

标签: ruby-on-rails ruby activerecord

我有以下关联:Artist has_many Songs。因此,我可以通过以下方式获得艺术家的歌曲:

artist.songs

但是,我想只获得歌曲'体裁:

artist.songs.pluck(:genre)

但是,这种类型可能会在结果中出现多次;我只想获得独特的流派价值观。不幸的是,pluck在这里没有任何帮助,因为它返回一个数组,并且在其上调用uniq不会调整ActiveRecord查询,但是普通Array#uniq

我可以这样做:

artist.songs.select(:genre).uniq.pluck(:genre)

但我觉得必须有更好的方法。

P.S。:从一些最小的基准测试来看,pluck + Array#uniq似乎比select + uniq + pluck快一点。

5 个答案:

答案 0 :(得分:5)

如果使用艺术家的songs关联,您可以在select distinctgenre,然后将结果映射到只返回字符串:

artist.songs.select('distinct genre').map(&:genre)
# or...
artist.songs.select(:genre).uniq.map(&:genre) # uniq or distinct work

结果查询:

(0.2ms) SELECT distinct genre FROM "songs" WHERE "songs"."artist_id" = ? [["artist_id", 1]]

如果在缩小到艺术家时直接调用Song模型,您也可以使用uniq:

Song.where(artist: artist).uniq.pluck(:genre)

结果查询:

(0.2ms) SELECT DISTINCT "songs"."genre" FROM "songs" WHERE "songs"."artist_id" = 1

两者都是同等有效的,并且在SQL中执行唯一性操作,而不是在Ruby中。

答案 1 :(得分:1)

我是这样做的。

artist.songs.pluck('DISTINCT genre')

我仍在寻找更好的方法。我觉得这比select(:col).uniq.pluck(:col)

更干净

答案 2 :(得分:1)

请注意,从Rails 5开始,Relation#uniq is deprecated; you should use Relation#distinct instead

我最近自己遇到了同样的问题。理想情况下,我希望以下代码可以工作 - 但它不会生成SQL来仅获取不同的值:

Song.where(artist: artist).distinct.pluck(:genre)

但是,作为一种解决方法,您可以改为:

SELECT DISTINCT "songs"."genre" FROM "songs" WHERE "songs"."artist_id" = 123

这将生成以下SQL:

class Artist < ApplicationRecord
  def genres
    Song.where(artist: self).distinct.pluck(:genre)
  end
end

为方便起见,您还可以考虑将其添加为模型方法,例如:

Array#map

这实现了最佳性能,因为查询完全在SQL中 - 没有使用诸如Array#uniq<div ng-repeat="item in probability"> <div ng-class="(1-item.prob.p)>=0.5 ? 'yellowClass':'redClass'">{{1-item.prob.p}} </div> 之类的ruby操作。

答案 3 :(得分:0)

Model.uniq.pluck(:genre)  

这会生成SQl查询SELECT DISTINCT,而不是再次向数组查询.uniq。

答案 4 :(得分:0)

这是其他答案的补充。如果要根据特定列选择唯一行,也可以使用

artist.pluck('distinct on (col1) col1, col2, col3')

基本上是

select distinct on (col1) col1, col2, col3 from artist

你也可以选择

来做到这一点

artist.select('distinct on (col1) col1, col2, col3')

pluck给出数组值,其中select给出了记录数组。