Rails:从一个列中选择唯一的值

281

2
另一个使用uniq的例子 - manish nautiyal
14个回答

553
Model.select(:rating)
这导致产生了一个 Model 对象的集合,而不是普通的评分。从 uniq 的角度来看,它们完全不同。你可以使用以下方法:
结果为:

这会生成一个Model对象的集合,而不是简单的评分。从 uniq 的角度来看,它们是完全不同的。你可以使用以下代码:

Model.select(:rating).map(&:rating).uniq

或者选择这个(最有效):

Model.uniq.pluck(:rating)

Rails 5+

Model.distinct.pluck(:rating)

更新

显然,在Rails 5.0.0.1中,它仅适用于像上面那样的“顶级”查询。在集合代理(例如“has_many”关系)上不起作用。

Address.distinct.pluck(:city) # => ['Moscow']
user.addresses.distinct.pluck(:city) # => ['Moscow', 'Moscow', 'Moscow']
在这种情况下,在查询之后进行去重。
user.addresses.pluck(:city).uniq # => ['Moscow']

74
值得注意的是,Model.uniq.pluck(:rating) 是最有效的方法 - 它会生成使用 SELECT DISTINCT 的 SQL,而不是对数组应用.uniq - Mikey
30
在Rails 5中,“Model.uniq.pluck(:rating)”将变为“Model.distinct.pluck(:rating)”。 - neurodynamic
3
如果您想从has_many关系中选择唯一值,您总是可以执行Model.related_records.group(:some_column).pluck(:some_column) - Krzysztof Karski
@SergioTulentsev 在 Rails 4.2.5 上,Model.distinct.pluck(:rating) 对我也有效。 - EliadL
无法在 Rails 7 上工作 - Nick Roz
显示剩余9条评论

102

如果你打算使用Model.select,那么最好直接使用DISTINCT,因为它只会返回唯一值。这样做更好,因为它意味着它返回的行数更少,应该比返回多行然后告诉Rails挑选唯一值更快。

Model.select('DISTINCT rating')

当然,前提是您的数据库能理解DISTINCT关键字,大多数数据库都可以。


6
使用Model.select("DISTINCT rating").map(&:rating)获取一个仅包含评分的数组。 - Kris
非常适合那些使用Rails 2.3的遗留应用程序的人。 - Mikey
3
是的,这很好用 - 但它只返回DISTINCT属性。如何在模型对象唯一时返回整个模型对象,以便在属性唯一的情况下可以访问模型中的所有属性。 - zero_cool
@Jackson_Sandland 如果你想要一个 Model 对象,那么它需要从表中的一条记录实例化。但是你并没有选择一条记录,只是选择了一个唯一的值(可能有多条记录)。 - Benissimo

78

这也有效。

Model.pluck("DISTINCT rating")

我相信pluck是Ruby 1.9.x及以上版本的功能。使用早期版本的用户将无法使用它。如果您使用的是1.9x及以上版本,Ruby文档也指出以下代码可以实现同样的效果:Model.uniq.pluck(:rating) - kakubei
7
pluck是一个纯粹的Rails 3.2以上版本方法,不依赖于Ruby 1.9.x。详情请参见http://apidock.com/rails/v3.2.1/ActiveRecord/Calculations/pluck。 - Daniel Rikowski
4
Rails 6.1 不再允许使用非属性参数,所以对于那些使用 6.1 及以上版本的用户,可以使用以下方法: Model.pluck(Arel.sql("DISTINCT rating"))。该方法可实现相同的功能。 - Rocket Appliances

40

如果您还想选择额外的字段:

Model.select('DISTINCT ON (models.ratings) models.ratings, models.id').map { |m| [m.id, m.ratings] }

1
选择额外字段 - cappie013

27
Model.uniq.pluck(:rating)

# SELECT DISTINCT "models"."rating" FROM "models"

这样做的好处是不使用SQL字符串和实例化模型。


4
这在 Rails 5.1 / AR 5.1 中会抛出一个错误 => undefined method `uniq' - Graham Slick

26
Model.select(:rating).uniq

这段代码起到了“DISTINCT”作用(不是Array#uniq),自 rails 3.2 起可用。

在 Rails 6 及以上版本中,应为:

Model.select(:rating).distinct

2
在Rails 6中(至少6.0.3),这不会生成DISTINCT子句,因此它是一个SELECT models.rating FROM models,然后使用Array#uniq。 - Rocket Appliances

14
Model.select(:rating).distinct

6
这是唯一官方正确的答案,而且非常有效。尽管在末尾添加.pluck(:rating)将使其完全符合 OP 的要求。 - Sheharyar

6

使用SQL收集唯一列的另一种方法:

Model.group(:rating).pluck(:rating)

因为提供了一个原始的解决方案,所以被点赞。根据数据库,这甚至可能比DISTINCT子句更高效。 - Rocket Appliances

4
如果我没理解错的话:

当前查询:


以下是需要翻译的内容,请问是否需要继续翻译?
Model.select(:rating)

返回的是对象数组,而你编写了查询。
Model.select(:rating).uniq

uniq 用于对象数组,每个对象都有唯一的 ID。uniq 的工作正常,因为数组中的每个对象都是唯一的。

选择不同的评级方法有很多种:

Model.select('distinct rating').map(&:rating)

或者
Model.select('distinct rating').collect(&:rating)

或者

Model.select(:rating).map(&:rating).uniq

或者
Model.select(:name).collect(&:rating).uniq

还有一件事,第一和第二个查询:通过SQL查询查找不同的数据。

这些查询将认为“london”和“london   ”相同,这意味着它会忽略空格,因此在查询结果中只选择“london”一次。

第三和第四个查询:

通过SQL查询查找数据,并应用ruby uniq方法来查找不同的数据。这些查询将认为“london”和“london ”不同,因此在查询结果中选择“london”和“london ”。

请参考附加的图片以获得更好的理解,并查看“已巡游/等待RFP”。

enter image description here


8
mapcollect是同一个方法的别名,不需要为两者都提供示例。 - Adam Lassek

3

有些答案没有考虑到OP想要一个值数组

其他答案在模型具有数千条记录时效果不佳

仍然,我认为一个好的答案是:

    Model.uniq.select(:ratings).map(&:ratings)
    => "SELECT DISTINCT ratings FROM `models` " 

首先,您需要生成一个模型数组(由于选择而减小大小),然后提取这些选定模型具有的唯一属性(评级)。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接