21

私は周りを見回しましたが、これに対する答えは見つかりませんでした。すべての回答には、GROUPBYを使用しなかったカウントが含まれていました。

背景: ActiveRecord.findのオプションを取得するページネーターがあります。:limitおよび:offsetオプションを追加し、クエリを実行します。また、レコードの総数(制限を除く)をカウントする必要がありますが、クエリに:groupオプションが含まれている場合があり、ActiveRecord.countは、GROUPBYによって返されたすべての行とそのカウントを返そうとします。Rails2.3.5でこれを行っています。

私が欲しいのは、ActiveRecord.countがGROUPBYによって返された行数を返すことです。

これの1つのインスタンスを示すサンプルコードを次に示します(すべてのタグを検索し、そのタグを持つ投稿の数で並べ替えるために使用されます)。

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()を'tags。*、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(*)

これは、行数ではなく、GROUPBY結果セット全体を返します。

これを回避する方法はありますか、それともGROUP BYを使用したクエリを説明するためにページネーターをハックする必要がありますか(そしてそれをどのように行うのでしょうか)?

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=>'DISTINCTtags.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

別の(ハッキーな)解決策:

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(*)ASpost_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を使用して呼び出すことができます。

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 に答える