设置交叉点或加入效率更高? Ruby on rails

时间:2016-03-24 01:14:01

标签: ruby-on-rails ruby rails-activerecord

所以我的群组模型有很多成员,有些成员是彼此的朋友,有些则不是。我想创建一个用户可以通过我的应用程序发出请求的控制器操作,它会返回如下所示的哈希:

{ group : { members: { friends: group_friends, others: group_others } } }

我通过ruby中的集合交叉完成了以下操作:

group_members = group.all_members
user_friends = current_user.all_friends
user_group_friends = group_members & user_friends
user_group_others = group_members - user_group_friends

我的问题是,由于这是红宝石 - 在性能方面,使用连接的数据库查询是否会更好?或者我应该保持原样....

我也很好奇这个查询会是什么样的......

仅供参考:这些是我正在与之合作的关系:

  Group
    has_many :members, class_name: "User"
    has_many :inverse_members....
    # all_members retrieves members who have added, been added

  User
    has_many :user_groups
    has_many :groups, :through => :user_groups
    has_many :friendships
    has_many :friends, :through => friendships
    has_many :inverse_friends....
    ....
    # all_friends retrieves all friends who have added, been added

  User_Groups
    belongs_to :user
    belongs_to :group

  Friendship
    belongs_to :user
    belongs_to :friend

到目前为止的查询:

#1

group_members = group.all_members # Instance method uses returns associated members
group_members_friends = group_members.where("? in friends OR ? in inverse_friends", current_user, current_user)

给出此错误

     # --- Caused by: ---
 # SQLite3::SQLException:
 #   no such table: friends

 ActiveRecord::StatementInvalid:
   SQLite3::SQLException: no such table: friends: SELECT "users".* FROM "users" INNER JOIN "user_groups" ON "users"."id" = "user_groups"."user_id" WHERE "user_groups"."group_id" = ? AND (1 in friends OR 1 in inverse_friends)

因为允许group.all_members发生的连接(User_Groups)不包含字段朋友(显然通过上述关联)...如何访问相关的每个成员(用户)并检查他们的朋友??

#2

group_members = group.all_members # Instance method uses returns associated members
group_members_friends = group_members.where("id in ?", current_user.all_friends)

给出了这个错误:

 # --- Caused by: ---
 # SQLite3::SQLException:
 #   near "2": syntax error

 ActiveRecord::StatementInvalid:
   SQLite3::SQLException: near "2": syntax error: SELECT "users".* FROM "users" INNER JOIN "user_groups" ON "users"."id" = "user_groups"."user_id" WHERE "user_groups"."group_id" = ? AND (user.id in 2,3)

2 个答案:

答案 0 :(得分:3)

在这种查询中,数据库引擎最有可能比Ruby快得多。连接操作由引擎直接提供,因此它们具有多年的本地语言优化功能。

即使编译了调用语言(例如Java或C ++),我认为依赖引擎实现的连接操作仍然会更快。根据经验,引擎及其工程师在大多数情况下都是聪明的(首先是因为它们 in )。

我与亚历山大·迪莫没有任何关系,但我推荐他的书Ruby Performance Optimization提供有关此类问题的提示,基准和其他内容。顺便提一下,他解决了这一点。

答案 1 :(得分:2)

在评估数据库的时候要知道的要点会更快:

  1. 对数据库执行查询的速度非常快(通常,设置正确时)。
  2. Ruby相对较慢。
  3. 可能耗费时间的是多次往返数据库(因此给avoid N+1 queries的建议)。并不是说执行每个查询所花费的时间很多 - 它是从Ruby到数据库的往返行程。
  4. 由于第1点和第2点,如果你想要表现,几乎总是一个很好的决定将你可以用的任何逻辑推迟到数据库,而不是ruby
  5. "过早优化是所有邪恶的根源"。所以,如果你正在做的事情正在发挥作用,那么在你遇到性能问题之前不要担心它。
  6. 现在,将此应用于您的情况:此时,您只收到了一些单独的查询,因此您对第3点的确定没问题。到目前为止您还没有提到性能问题,所以你可能很好:第5点,应该保持原样。

    但是如果你想继续(可能是过早的)优化:

    关于第4点 - 您是否将逻辑推迟到数据库?

    好吧,如果您查看自ActiveRecord::Relation使用的方法,他们实际上只是委托给records。 (你可以通过例如group_members.all.method(:-).source_location然后查看源代码来找到它。现在,通过运行:recrods检查group_members.records.class.name是什么,得到Array - 也就是说,-&方法正在对Ruby结果数组执行操作 - 所以,这意味着减法/加法逻辑正在Ruby中发生,并且不会被推迟到数据库。这意味着如果您处理大量结果,它可能会很慢。

    就改变这一点而言,最初的预感可能是尝试在一个查询中获得所有结果 - 但这是一个坏主意,因为您仍然需要逐个使用Ruby过滤它们,实例化许多ruby对象,将它们放入各自的类别中。所以这里的逻辑仍然发生在ruby中。

    如果您想优化性能,您要做的就是有4个数据库查询 - 每个类别一个 - 但每个查询都会返回该类别的整个结果集,以避免您执行{{1}或者类似于Ruby。

    这只是伪代码,我不知道你的域名,但是像:

    group_members & user_friends

    ...并且您为每个类别都这样做。

相关问题