顺序扫描而不是索引扫描

时间:2015-07-16 07:42:22

标签: postgresql join optimization

我在postgresql中有一堆表,我按如下方式运行查询

 SELECT DISTINCT ON ...some stuff... 
    FROM "rent_flats" INNER JOIN "rent_flats_linked_users" 
            ON "rent_flats_linked_users"."rent_flat_id" = "rent_flats"."id" 
        INNER JOIN "users" 
            ON "users"."id" = rent_flats_linked_users"."user_id" 
        INNER JOIN "owners" 
            ON "owners"."id" = "users"."profile_id" AND "users"."profile_type" = 'Owner' 
        INNER JOIN "phone_numbers" 
            ON "phone_numbers"."person_id" = "owners"."id" AND "phone_numbers"."person_type" = 'Owner' 
        INNER JOIN "phone_number_categories" 
            ON "phone_number_categories"."id" = "phone_numbers"."phone_number_category_id" 
        INNER JOIN "localities" 
            ON "localities"."id" = "rent_flats"."locality_id" 
        INNER JOIN "regions" 
            ON "regions"."id" = "localities"."region_id" 
        INNER JOIN "cities" 
            ON "cities"."id" = "regions"."city_id" 
        INNER JOIN "property_types" 
            ON "property_types"."id" = "rent_flats"."property_type_id" 
        INNER JOIN "apartment_types" 
            ON "apartment_types"."id" = "rent_flats"."apartment_type_id" 
    WHERE "rent_flats"."status" = 3 
        AND (((extract(epoch from age(current_date,rent_flats.date_added))/86400)::int) IN (cities.short_period,cities.long_period)) 
        AND (phone_number_categories.name IN ('SMS','SMS & Mobile')) 
    ORDER BY rf_id, phone_numbers.priority ASC

注意: rent_flats表包含大约500万行,rent_flats_linked_users包含大约600k行,用户包含350k行。其他表格很小。

查询大约需要6.8秒才能执行,解释分析显示,大约50%的总时间用于连续扫描rent_flats,users和rent_flats_linked_users表,另外30%用于Hash连接。

将seq_scan设置为off ...查询需要更长时间~11秒(在这种情况下,Hash和Hash连接占用时间的97.5%)

Here's the explain query plan analyses. 我已将索引放在内部联接中涉及的字段以及过滤器中涉及的字段上,例如phone_numbers.priority和cities.short_period以及cities.long_period。但我仍然得到顺序扫描。可以解释查询的原因和可能的解决方案是什么?

1 个答案:

答案 0 :(得分:1)

我怀疑如果该查询的一部分值得优化,那就是:

(((extract(epoch from age(current_date,rent_flats.date_added))/86400)::int) IN (cities.short_period,cities.long_period))

你真的需要把它变成像:

rent_flats.date_added in (...)

然后你可以索引date_added,也可以索引(date_added,status)。

下一步是确保连接列已编入索引。