Sql 努力优化rails中不在查询中的rails

Sql 努力优化rails中不在查询中的rails,sql,ruby-on-rails,postgresql,optimization,query-optimization,Sql,Ruby On Rails,Postgresql,Optimization,Query Optimization,以下是rails中的查询: User.limit(20). where.not(id: to_skip, number_of_photos: 0). where(age: @user.seeking_age_min..@user.seeking_age_max). tagged_with(@user.seeking_traits, on: :trait, any: true). tagged_with(@user.seeking_gender, on: :trait, any:

以下是rails中的查询:

User.limit(20).
  where.not(id: to_skip, number_of_photos: 0).
  where(age: @user.seeking_age_min..@user.seeking_age_max).
  tagged_with(@user.seeking_traits, on: :trait, any: true).
  tagged_with(@user.seeking_gender, on: :trait, any: true).ids
下面是
EXPLAIN ANALYZE
的输出。注意
id ALL(…)
部分被缩短。其中大约有10K个ID

Limit  (cost=23.32..5331.16 rows=20 width=1698) (actual time=2237.871..2243.709 rows=20 loops=1)
  ->  Nested Loop Semi Join  (cost=23.32..875817.48 rows=3300 width=1698) (actual time=2237.870..2243.701 rows=20 loops=1)
        ->  Merge Semi Join  (cost=22.89..857813.95 rows=8311 width=1702) (actual time=463.757..2220.691 rows=1351 loops=1)
              Merge Cond: (users.id = users_trait_taggings_356a192.taggable_id)
              ->  Index Scan using users_pkey on users  (cost=0.29..834951.51 rows=37655 width=1698) (actual time=455.122..2199.322 rows=7866 loops=1)
                    Index Cond: (id IS NOT NULL)
                    Filter: ((number_of_photos <> 0) AND (age >= 18) AND (age <= 99) AND (id <> ALL ('{7066,7065,...,15624,23254}'::integer[])))
                    Rows Removed by Filter: 7652
              ->  Index Only Scan using taggings_idx on taggings users_trait_taggings_356a192  (cost=0.42..22767.59 rows=11393 width=4) (actual time=0.048..16.009 rows=4554 loops=1)
                    Index Cond: ((tag_id = 2) AND (taggable_type = 'User'::text) AND (context = 'trait'::text))
                    Heap Fetches: 4554
        ->  Index Scan using index_taggings_on_taggable_id_and_taggable_type_and_context on taggings users_trait_taggings_5df4b2a  (cost=0.42..2.16 rows=1 width=4) (actual time=0.016..0.016 rows=0 loops=1351)
              Index Cond: ((taggable_id = users.id) AND ((taggable_type)::text = 'User'::text) AND ((context)::text = 'trait'::text))
              Filter: (tag_id = ANY ('{4,6}'::integer[]))
              Rows Removed by Filter: 2
Total runtime: 2243.913 ms
to_skip
是一个不可跳过的用户ID数组。
用户
有许多
跳过
。每个
skip
都有一个
partner\u id

因此,要获取
以跳过
我正在做:

to_skip = @user.skips.pluck(:partner_id)
我试图将查询隔离为:

sql = User.limit(20).
  where.not(id: to_skip, number_of_photos: 0).
  where(age: @user.seeking_age_min..@user.seeking_age_max).to_sql
而且在解释分析中仍然遇到同样的问题。再次,用户ID列表被截断:

Limit  (cost=0.00..435.34 rows=20 width=1698) (actual time=0.219..4.844 rows=20 loops=1)
  ->  Seq Scan on users  (cost=0.00..819629.38 rows=37655 width=1698) (actual time=0.217..4.838 rows=20 loops=1)
        Filter: ((id IS NOT NULL) AND (number_of_photos <> 0) AND (age >= 18) AND (age <= 99) AND (id <> ALL ('{7066,7065,...,15624,23254}'::integer[])))
        Rows Removed by Filter: 6
Total runtime: 5.044 ms
限制(成本=0.00..435.34行=20宽=1698)(实际时间=0.219..4.844行=20圈=1)
->按用户顺序扫描(成本=0.00..819629.38行=37655宽度=1698)(实际时间=0.217..4.838行=20圈=1)
过滤器:((id不为空)和(照片数量0)和(年龄>=18)和(年龄:销毁)
acts_as_taggable#acts_as_taggable的别名:tags
扮演角色:寻找性别、特质、种族
范围:截止日期,->{
订单(“在描述处更新”)
}
结束
#模式
创建“用户”表,force::cascade do | t|
t、 字符串“email”,默认值:,null:false
t、 datetime“created_at”,null:false
t、 datetime“更新时间”,null:false
t、 文本“跳过”,数组:true
t、 整数“照片数量”,默认值:0
t、 整数“年龄”
结束
添加索引“users”[“age”],名称:“index_users_on_age”,使用::btree
添加索引“users”[“email”],名称:“index_users_on_email”,唯一:true,using::btree
添加索引“用户”[“照片数量”],名称:“在照片数量上索引用户”,使用::btree
添加索引“users”[“updated_at”],名称:“index_users_on_updated_at”,顺序:{“updated_at”=>:desc},使用::btree

类跳过
速度问题可能是由于
中的一长串ID(大约60Kb)被作为数组传递进来。解决方法是将其重新加工成子查询的结果,以便postgress可以更好地优化查询

在构建要跳过的
时,尝试使用
select
而不是
pulk
pulk
返回一个数组,然后将该数组传递给主查询。
select
依次返回
ActiveRecord::Relation
,主查询中可以包含该数组的sql,这可能会提高查询的效率

to_skip = @user.skips.select(:partner_id)
在发布模型代码之前,很难提出更具体的建议。我将探讨的一般方向是尝试将所有相关步骤合并到一个查询中,以让数据库进行优化

更新

使用
select
的活动记录查询看起来像这样(我跳过了
taggable
的内容,因为它似乎对性能影响不大):

这是执行的SQL查询。请注意子查询如何获取要跳过的ID:

SELECT  "users".* FROM "users"
  WHERE ("users"."number_of_photos" != 0)
    AND ("users"."id" NOT IN (
      SELECT "skips"."partner_id"
        FROM "skips"
        WHERE "skips"."user_id" = 1
    ))
    AND ("users"."age" BETWEEN 0 AND 25)
  LIMIT 20

尝试以这种方式运行查询,看看它对性能有何影响。

速度问题可能是由于
中的一长串ID以数组形式传递过来(大约60Kb)。然后,解决方案是将其作为子查询的结果进行重制,以便postgress可以更好地优化查询

在构建要跳过的
时,尝试使用
select
而不是
pulk
pulk
返回一个数组,然后将该数组传递给主查询。
select
依次返回
ActiveRecord::Relation
,主查询中可以包含该数组的sql,这可能会提高查询的效率

to_skip = @user.skips.select(:partner_id)
在发布模型代码之前,很难提出更具体的建议。我将探讨的一般方向是尝试将所有相关步骤合并到一个查询中,以让数据库进行优化

更新

使用
select
的活动记录查询看起来像这样(我跳过了
taggable
的内容,因为它似乎对性能影响不大):

这是执行的SQL查询。请注意子查询如何获取要跳过的ID:

SELECT  "users".* FROM "users"
  WHERE ("users"."number_of_photos" != 0)
    AND ("users"."id" NOT IN (
      SELECT "skips"."partner_id"
        FROM "skips"
        WHERE "skips"."user_id" = 1
    ))
    AND ("users"."age" BETWEEN 0 AND 25)
  LIMIT 20

尝试以这种方式运行您的查询,看看它如何影响性能。

请发布所有相关型号的代码。添加了它们。请让我知道它们是否足够。您有一个专用的
跳过
型号和
用户。跳过
数组字段。后者的原因是什么?请发布所有相关型号的代码。添加请让我知道它们是否足够。您有一个专用的
Skip
模型和
用户。skips
数组字段。后者的原因是什么?谢谢。我会尝试一下。同时我还添加了模型注释+模式。实际上,现在如何使用select(:partner_id)编写查询?to_skip=@user.skips.select(:partner_id)results=user.limit(20)。where.not(id:to_skip)。ids似乎不起作用。我复制并粘贴了精确的查询,收到了0个结果。这是查询的“to_sql”:从“用户”中选择“用户”。*其中(“用户”。“照片数量”!=0)和(“用户”。“id”不在(选择“skips”。“partner_id”中)“skips”其中“skips”。“user_id”=23254)和(“users”。“age”介于0和25之间)限制20当我将查询改为使用pulk时,user.LIMIT(20)。WHERE.not(id:@user.skips.pulk(:partner_id),照片的数量:0)。其中(age:0..25)按预期工作并返回20项。它看起来像是rails“WHERE.not”“无法正常工作。当我将查询更改为仅使用“where”时,它将正常工作并按预期返回跳过的对象。但是
User.limit(20).
  where.not(id: @user.skips.select(:partner_id), number_of_photos: 0).
  where(age: 0..25)
SELECT  "users".* FROM "users"
  WHERE ("users"."number_of_photos" != 0)
    AND ("users"."id" NOT IN (
      SELECT "skips"."partner_id"
        FROM "skips"
        WHERE "skips"."user_id" = 1
    ))
    AND ("users"."age" BETWEEN 0 AND 25)
  LIMIT 20