如何将两个不同ActiveRecord关系的COUNT(*)合并到一个SQL查询中?

时间:2019-01-22 16:13:56

标签: ruby-on-rails rails-activerecord

使用两个不同的ActiveRecord关系对象,是否可以发出一个SQL查询来比较关系的计数?

例如说我有两个ActiveRecord :: Relation对象,像这样:

posts = Post.where().where().some_scope
users = User.where().some_other_scope.where().joins(:something)

要比较每个关系的计数,我必须发出两个SQL查询。

posts.count == users.count
# => SELECT COUNT(*) FROM posts WHERE... ;
# => SELECT COUNT(*) FROM users INNER JOIN ... WHERE... ;

我只希望发出一个查询。像这样:

Post.select("COUNT(first) == COUNT(second) as are_equal"), posts, users).are_equal

3 个答案:

答案 0 :(得分:4)

除非您使用UNION,否则不可能将两个不同表的两个计数合并到一个查询中。它将运行两个单独的查询并合并结果。这与分别运行两个查询大约需要相同的时间,只需要一次进入db-server(1个查询),但是会降低可读性。所以恕我直言,我真的想知道这是否值得。

例如在一种情况下,您可以编写

 if posts.count == users.count 

在另一种情况下,会写:

 count_sql = <<-SQL 
    select "Posts" as count_type, count(*) from posts where ... 
    union 
    select "Users" as count_type, count(*) from users where ...
 SQL 

 result = Post.connection.execute(count_sql) 
 if result[0]["count"] == result[1]["count"] 

您将不得不决定性能改进是否可以达到降低可读性的目的。

答案 1 :(得分:1)

这对于ActiveRecord查询方法是不可能的,但是底层的Arel查询构建器(ActiveRecord在内部使用)可以实现此目的,但看起来不太优雅:

posts = Post.where().where().some_scope
users = User.where().some_other_scope.where().joins(:something)

posts_table = Post.arel_table
users_table = User.arel_table

posts_count = Arel::Nodes::Count.new([posts_table[:id]]).as('count')
users_count = Arel::Nodes::Count.new([users_table[:id]]).as('count')

union = posts.select(posts_count).arel.union(users.select(users_count).arel)

post_count, user_count = Post.from(posts_table.create_table_alias(union, :posts)).map(&:count)

尽管在这种情况下它实际上可能没有任何好处(如其他答案中所述),但值得一提的是Arel,因为在某些情况下它很有用-我始终尝试在Rails应用程序中避免使用原始SQL,Arel会可能的。

可以在这里找到出色的介绍:https://danshultz.github.io/talks/mastering_activerecord_arel/#/

答案 2 :(得分:0)

您始终可以编写自己的SQL查询。

假设您有两种模型,AdminUser和Company。一种执行所需操作的方法如下:

ActiveRecord::Base.connection.execute("SELECT COUNT(*) as nb from admin_users UNION SELECT COUNT(*) as nb from companies;").to_a

最后将得到两个哈希数组,每个哈希包含每个数据库表的记录数。