使用两个不同的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
答案 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
最后将得到两个哈希数组,每个哈希包含每个数据库表的记录数。