2

這裏有三個連續的查詢與他們的基準性能:如何優化查詢成千上萬的ID

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" 

我該如何解決這裏發生的事情?

回答

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,而並不真正需要的​​集合,可以使這稍微像這樣高效:

@email = Person.joins(:projects).merge(@Company.projects.submitted). 
        where("LENGTH(persons.email) > 0").pluck(:email)