【问题标题】:How can I combine COUNT(*) for two different ActiveRecord relations into a single SQL query?如何将两个不同 ActiveRecord 关系的 COUNT(*) 组合到一个 SQL 查询中?
【发布时间】:2019-01-22 16:13:55
【问题描述】:

对于两个不同的 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

【问题讨论】:

    标签: ruby-on-rails rails-activerecord


    【解决方案1】:

    不可能将两个不同表的两个计数合并到一个查询中,除非您使用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"] 
    

    您必须决定性能改进是否会导致可读性下降。

    【讨论】:

    • 很好的解释,谢谢。可能不值得少往返一次数据库。
    【解决方案2】:

    这对于 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/#/

    【讨论】:

      【解决方案3】:

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

      假设您有两个模型,AdminUser 和 Company。一种做你想做的事情的方法如下:

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

      您最终会得到一个由两个散列组成的数组,每个散列包含每个数据库表的记录数。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-10-08
        • 2015-06-25
        • 1970-01-01
        • 2019-01-09
        • 2021-10-09
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多