Rails 3: разница между Relation.count и Relation.all.count - PullRequest
6 голосов
/ 23 февраля 2011

Moin

Я наткнулся на несоответствие в ActiveRecord.
Я попытался получить все используемые комбинации значений в двух столбцах большой таблицы. Первая идея:

SELECT DISTINCT col1, col2 FROM table

Представьте себе приложение для рельсов, которое организует питание в качестве модели и каждого блюда has_many :noodles Каждая лапша имеет атрибуты (и, следовательно, столбцы таблицы БД) color и shape. Моя цель - получить количество всех имеющихся комбинаций color и shape для одного приема пищи.

Поскольку AR не предоставляет "отличный" метод, который я использовал

my_meal.noodles.select("distinct color, shape")

и получил (в консоли rails консоль) вывод из шести строк из 8 объектов Noodle (соответственно их представления String). Но:

>> my_meal.noodles.select("distinct color, shape").count
=> 1606

На самом деле my_meal содержит 1606 лапшу. Если я преобразую отношение в массив и получу его размер или использую .all.count, результат будет правильным.

Итак, мой вопрос: почему AR выводит 8 объектов, но считает все строки БД?

Подобная проблема , кажется, упоминается здесь , но ответа не дано.

Спасибо и всего наилучшего, Тим

1 Ответ

12 голосов
/ 24 февраля 2011

Хорошо, спасибо tadman за то, что подтолкнули меня в правильном направлении.

Я копал немного глубже (особенно в файлах журналов), и то, что я нашел, немного странно.

Проблема была вызвана количеством выбранных столбцов. Если выбрать только один столбец и считать результат

my_meal.noodles.select("distinct color").count

ActiveRecord создает следующий оператор SQL:

SELECT COUNT(distinct color) AS count_id FROM "NOODLES" WHERE ("NOODLES".meal_id = 295)

В случае, если один выбирает два или более столбца и применяет к нему count

my_meal.noodles.select("distinct color, shape").count

ActiveRecord забывает об этом предложении select и создает:

SELECT COUNT(*) AS count_id FROM "NOODLES" WHERE ("NOODLES".meal_id = 295)

Это может быть правильно, поскольку (SQL) COUNT допускает использование только одного или нескольких столбцов в качестве параметров. Добавьте group перед count и все в порядке:

my_meal.noodles.select("distinct color, shape").group("color, shape").count

SELECT COUNT(*) AS count_all, color, shape AS color_shape FROM "NOODLES" WHERE ("NOODLES".meal_id = 295) GROUP BY color, shape

Помимо этого AS color_shape это именно то, что я ожидал. НО ... только это возвращает это:

>> my_meal.noodles.select("distinct color, shape").group("color, shape").count
=> {star=>309, circle=>111, spaghetti=>189, square=>194, triangle=>179, bowtie=>301, shell=>93, letter=>230}

>> my_meal.noodles.select("distinct color, shape").group("color, shape").count.class
=> ActiveSupport::OrderedHash

Это странное возвращаемое значение (кроме порядка, зависящего от БД) идентично результату и возвращаемому значению

my_meal.noodles.group("shape").count

Вывод:
Как указывало , здесь все еще существует разрыв между отношениями (могут быть математическими или относительными) и ActiveRecord :: Relations.
Я вижу преимущества использования результата в шаблонах модели как можно чаще (по крайней мере, в контексте приложения Rails).
Однако реальные отношения являются не результатом комбинации нескольких операций, а результатом объединения этих операций. В целом, цепочка ActiveRecord :: Relations - отличная вещь, но есть некоторые конструктивные решения, которым я не могу следовать.
Если вы не можете полагаться на уверенность в том, что каждое действие возвращает новое отношение для работы, оно теряет большую часть своей первоначальной привлекательности.

Что касается решения моей проблемы, я буду использовать вышеупомянутое решение group и какой-то грязный обходной путь для операции подсчета:

my_meal.noodles.select("distinct color, shape").group("color, shape").all.count

Это сжимает результаты до приемлемого минимума, прежде чем извлекать их из базы данных и создавать дорогие объекты, чтобы просто посчитать их. В качестве альтернативы можно использовать рукописный SQL-запрос, но зачем иметь Rails и не использовать его, а? ; -)

Спасибо за помощь,
Тим

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...