Подтвердить что ты не робот

ActiveRecord Count для подсчета строк, возвращаемых группой в Rails

Я огляделся и не мог найти ответы на это. Все ответы включали подсчеты, в которых не использовали GROUP BY.

Фон: У меня есть paginator, который будет использовать опции для ActiveRecord.find. Он добавляет параметр: limit и: offset и выполняет запрос. То, что мне также нужно сделать, - подсчитать общее количество записей (меньше предела), но иногда запрос содержит параметр: group, а ActiveRecord.count пытается вернуть все строки, возвращаемые GROUP BY вместе с каждым их количеством. Я делаю это в Rails 2.3.5.

Я хочу, чтобы ActiveRecord.count возвращал количество строк, возвращаемых GROUP BY.

Вот пример кода, который демонстрирует один экземпляр этого (используется для поиска всех тегов и их упорядочения по количеству сообщений с этим тегом):

options = { :select => 'tags.*, COUNT(*) AS post_count',
            :joins => 'INNER JOIN posts_tags',   #Join table for 'posts' and 'tags'
            :group => 'tags.id',
            :order => 'post_count DESC' }

@count = Tag.count(options)

options = options.merge { :offset => (page - 1) * per_page, :limit => per_page }

@items = Tag.find(options)

С помощью опции: select, Tag.count генерирует следующий SQL:

SELECT count(tags.*, COUNT(*) AS post_count) AS count_tags_all_count_all_as_post_count, tags.id AS tags_id FROM `tags`  INNER JOIN posts_tags  GROUP BY tags.id  ORDER BY COUNT(*) DESC

Как вы можете видеть, это просто завернуло COUNT() вокруг тегов., COUNT (*) 'и MySQL жалуется на COUNT в COUNT.

Без опции: select он генерирует этот SQL:

SELECT count(*) AS count_all, tags.id AS tags_id FROM `tags` INNER JOIN posts_tags GROUP BY tags.id ORDER BY COUNT(*)

который возвращает весь набор результатов GROUP BY, а не количество строк.

Есть ли способ обойти это или мне придется взломать paginator для учета запросов с помощью GROUP BY (и как я буду это делать)?

4b9b3361

Ответ 1

Обходной путь для моей ситуации, похоже, заключается в замене: group = > 'tags.id' на: select = > 'DISTINCT tags.id' в хэш-настройке параметров перед выполнением подсчета.

count_options = options.clone
count_options.delete(:order)

if options[:group]
  group_by = count_options[:group]
  count_options.delete(:group)
  count_options[:select] = "DISTINCT #{group_by}"
end

@item_count = @type.count(count_options)

Ответ 2

Похоже, вам нужно обрабатывать сгруппированные запросы отдельно. Выполнение подсчета без группы возвращает целое число, а подсчет с группой возвращает хеш:

Tag.count
  SQL (0.2ms)  SELECT COUNT(*) FROM "tags"
 => 37

Tag.count(:group=>"tags.id")
  SQL (0.2ms)  SELECT COUNT(*) AS count_all, tags.id AS tags_id FROM "tags" 
    GROUP BY tags.id
 => {1=>37}

Ответ 3

Если вы используете Rails 4 или 5, вы также можете сделать следующее.

Tag.group(:id).count

Ответ 4

Другое (хакерское) решение:

selection = Tag.where(...).group(...)
count = Tag.connection.select_value "select count(*) from (" + selection.to_sql + ") as x"

Ответ 5

Если я правильно понял ваш вопрос, тогда он должен работать, если вы вообще не используете Tag.count. Задание "COUNT (*) AS post_count" в выбранном хеше должно быть достаточно. Например:

@tag = Tag.first(options)
@tag.post_count

Как вы можете видеть, значение post_count из запроса доступно из экземпляра @tag. И если вы хотите получить все теги, то возможно что-то вроде этого:

@tags = Tag.all(options)
@tags.each do |tag|
  puts "Tag name: #{tag.name} posts: #{tag.post_count}"
end

Update:

Можно вызвать счетчик, с которым атрибут будет подсчитываться, а также параметр: distinct

options = { :select => 'tags.*, COUNT(*) AS post_count',
            :joins => 'INNER JOIN posts_tags',   #Join table for 'posts' and 'tags'
            :group => 'tags.id',
            :order => 'post_count DESC',
            :offset => (page - 1) * per_page,
            :limit => per_page }

@count = Tag.count(:id, :distinct => true, :joins => options[:joins])

@items = Tag.find(options)