Рельсы: выберите уникальные значения из столбца


238

У меня уже есть рабочее решение, но мне бы очень хотелось узнать, почему это не работает:

ratings = Model.select(:rating).uniq
ratings.each { |r| puts r.rating }

Он выбирает, но не печатает уникальные значения, он печатает все значения, включая дубликаты. И это в документации: http://guides.rubyonrails.org/active_record_querying.html#selecting-specific-fields


2
Другой пример с uniq stackoverflow.com/questions/8369812/…
manish nautiyal

Ответы:


449
Model.select(:rating)

Результатом этого является коллекция 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']

Я сделал: group (: rating) .collect {| r | r.rating} Поскольку map == collect, где я могу прочитать об используемом вами синтаксисе (&: rating)? Я не вижу этого в документации Руби.
alexandrecosta

@ user1261084: см. Symbol # to_proc, чтобы понять .map (&: rating). PragDave объясняет
dbenhur

63
Стоит отметить, что Model.uniq.pluck(:rating)это самый эффективный способ сделать это - генерировать SQL, который использует, SELECT DISTINCTа не применяет .uniqк массиву
Mikey

23
В Rails 5 Model.uniq.pluck(:rating)будетModel.distinct.pluck(:rating)
нейродинамика

2
Если вы хотите выбрать уникальные значения из отношения has_many, вы всегда можете это сделатьModel.related_records.group(:some_column).pluck(:some_column)
Кшиштоф Карский

92

Если вы собираетесь использовать Model.select, то вы можете просто использовать DISTINCT, поскольку он будет возвращать только уникальные значения. Это лучше, потому что это означает, что он возвращает меньше строк и должен быть немного быстрее, чем возвращать количество строк, а затем указывать Rails выбрать уникальные значения.

Model.select('DISTINCT rating')

Конечно, это при условии, что ваша база данных понимает DISTINCTключевое слово, и большинство должно.


6
Model.select("DISTINCT rating").map(&:rating)чтобы получить массив только рейтинги.
Крис

Отлично подходит для тех, у кого старые приложения используют Rails 2.3
Mikey

3
Да ... это работает просто фантастически, но возвращает только атрибут DISTINCT. Как вы можете вернуть весь объект Model, если он отличается? Так что у вас будет доступ ко всем атрибутам в модели в тех случаях, когда атрибут уникален.
zero_cool

@Jackson_Sandland Если вам нужен объект Model, его необходимо создать из записи в таблице. Но вы не выбираете запись только уникальное значение (из того, что может быть несколько записей).
Бениссимо

69

Это тоже работает.

Model.pluck("DISTINCT rating")

Я считаю, что отрывок - это Ruby 1.9.x и выше. Любой, использующий предыдущую версию, не будет иметь ее. Если вы находитесь в 1.9x и выше, документы по ruby ​​говорят, что это тоже работает: Model.uniq.pluck (: rating)
kakubei

6
pluckэто чистый метод Rails> 3.2, который не зависит от Ruby 1.9.x См. apidock.com/rails/v3.2.1/ActiveRecord/Calculations/pluck
Даниэль Риковски,

34

Если вы хотите также выбрать дополнительные поля:

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

1
select extra fields<3 <3
cappie013

27
Model.uniq.pluck(:rating)

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

В этом есть преимущества не использовать строки SQL и не создавать экземпляры моделей


3
Это приводит к ошибке с Rails 5.1 / AR 5.1 => неопределенный метод `uniq '
Грэм Слик

24
Model.select(:rating).uniq

Этот код работает как 'DISTINCT' (не как Array # uniq) начиная с rails 3.2


5
Model.select(:rating).distinct

2
Это единственный официально правильный ответ, который также очень эффективен. Хотя добавление .pluck(:rating)в конце сделает это именно тем, о чем просил ОП.
Шехарьяр

5

Если я иду прямо в путь, то:

Текущий запрос

Model.select(:rating)

возвращает массив объектов, и вы написали запрос

Model.select(:rating).uniq

Uniq применяется к массиву объекта, и каждый объект имеет уникальный идентификатор. uniq выполняет свою работу правильно, потому что каждый объект в массиве 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-запросу.

Эти запросы будут рассматриваться как «Лондон» и «Лондон» одинаково, то есть они будут пренебрегать пробелом, поэтому он выберет «Лондон» один раз в результате запроса.

Третий и четвертый запрос:

поиск данных по запросу SQL и для отдельных данных применяется метод ruby ​​uniq. эти запросы будут считать «лондон» и «лондон» разными, поэтому в результатах запроса будут выбраны «лондон» и «лондон».

Пожалуйста, для большей ясности используйте прикрепленное изображение и посмотрите «Toured / Awaiting RFP».

введите описание изображения здесь


6
map& collectявляются псевдонимами для одного и того же метода, нет необходимости приводить примеры для обоих.
Адам Лассек

4

В некоторых ответах не учитывается, что ОП хочет массив значений

Другие ответы не работают, если ваша модель имеет тысячи записей

Тем не менее, я думаю, что хороший ответ:

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

Потому что сначала вы генерируете массив Model (с уменьшенным размером из-за выбора), затем вы извлекаете единственный атрибут, который имеют эти выбранные модели (оценки)


3

Если кто-то ищет то же самое с Mongoid, то есть

Model.distinct(:rating)

этот не работает сейчас, теперь он возвращает кратные.
EUPHORAY

не возвращает отчетливый
dowi

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.