21

我环顾四周,找不到任何答案。所有答案都涉及使用 GROUP BY 的计数。

背景: 我有一个分页器,它将为 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

正如你所看到的,它只是在'tags.*, 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 结果集而不是行数。

有没有办法解决这个问题,或者我是否必须破解分页器来解释使用 GROUP BYs 的查询(我将如何去做)?

4

5 回答 5

23

似乎您需要单独处理分组查询。不带组的计数返回一个整数,而带组的计数返回一个哈希:

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}
于 2011-02-22T02:33:57.913 回答
18

如果您使用的是 Rails 4 或 5,您也可以执行以下操作。

Tag.group(:id).count
于 2014-09-27T17:20:36.583 回答
9

我的情况的解决方法似乎是在执行计数之前将选项哈希中的 :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)
于 2011-02-22T13:00:11.450 回答
3

另一个(hacky)解决方案:

selection = Tag.where(...).group(...)
count = Tag.connection.select_value "select count(*) from (" + selection.to_sql + ") as x"
于 2015-04-13T11:42:46.910 回答
2

如果我正确理解了您的问题,那么如果您根本不使用 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

更新:

可以使用要计数的属性以及参数 :distinct 调用 Count

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)
于 2011-02-22T02:02:41.143 回答