以下是连续三次基准测试的查询:
ids = @Company.projects.submitted.uniq.collect(&:person_id)
1.370000 0.060000 1.430000 ( 3.763946)
@persons = Person.where("id IN (?)", ids)
0.030000 0.000000 0.030000 ( 0.332878)
@emails = @persons.collect(&:email).reject(&:blank?)
16.550000 1.640000 18.190000 (128.002465)
ids
包含近10000个ID,并在运行我看到的最后一个查询时:
SELECT "persons".* FROM "persons" WHERE (id in (121,142,173,178...14202))
(*1000s ->) User Load (13.0ms) SELECT "users".* FROM "users" WHERE "users"."roleable_type" = 'Person' AND "users"."roleable_id" = 121 LIMIT 1
Indexes on User:
add_index "users", ["roleable_id", "roleable_type"], :name => "index_users_on_roleable_id_and_roleable_type"
add_index "users", ["roleable_type", "roleable_id"], :name => "index_users_on_roleable_type_and_roleable_id"
我如何解决这里发生的事情?
答案 0 :(得分:1)
你拥有它的第二个查询实际上并没有击中数据库。它正在构建一个ActiveRecord::Relation
(一个懒惰的查询),在调用第三个查询之前不会触发它。您可以通过在第二个查询的末尾添加.all
来证明这一点。
要解决性能问题,您希望使用文字列表删除IN ()
,因为这会严重损害大型列表的数据库性能:
@persons = Person.joins(:projects).merge(@Company.projects.submitted)
您也可以使用子查询(尽管效率低于JOIN)来执行此操作:
subquery = @Company.projects.submitted.select("projects.person_id").to_sql
@persons = Person.where("id IN (#{subquery})")
如果您只想获得结果@emails
,并且真的不需要@persons
集合,那么您可以使其效率更高一些:
@email = Person.joins(:projects).merge(@Company.projects.submitted).
where("LENGTH(persons.email) > 0").pluck(:email)