提高JOIN和子SELECT的查询性能

时间:2016-07-27 16:15:24

标签: database postgresql

我有一个相当复杂的查询,它有多个内部联接以及一个子选择。它表现得非常糟糕:

EXPLAIN ANALYZE SELECT
   SUM(weekly_baskets.servings / 2) AS sum_weekly_baskets_servings_2,
   meal_selections.meal_id AS meal_selections_meal_id 
FROM
   "meal_selections" 
INNER JOIN
   "weekly_baskets" "weekly_baskets_meal_selections" 
      ON "weekly_baskets_meal_selections"."id" = "meal_selections"."weekly_basket_id" 
INNER JOIN
   "weekly_baskets" 
      ON "meal_selections"."weekly_basket_id" = "weekly_baskets"."id" 
WHERE
   "weekly_baskets"."menu_id" = 344 
   AND (
      NOT (EXISTS (SELECT
         1 
      FROM
         "cancellations"  
      WHERE
         ("cancellations"."cancellable_id" = "weekly_baskets"."id") 
         AND ("cancellations"."cancellable_type" = 'WeeklyBasket') 
         AND "cancellations"."active" = 't'))
   ) 
GROUP BY
   meal_selections.meal_id

以下是查询计划:

HashAggregate  (cost=122273.42..122275.79 rows=787 width=8) (actual time=32313.598..32313.602 rows=13 loops=1)
  Group Key: meal_selections.meal_id
  ->  Nested Loop  (cost=26501.36..122208.17 rows=43504 width=8) (actual time=97.199..32292.526 rows=48594 loops=1)
        ->  Nested Loop  (cost=26501.28..119314.73 rows=19567 width=12) (actual time=97.175..20446.310 rows=16067 loops=1)
              ->  Nested Loop Anti Join  (cost=26501.19..87846.01 rows=19567 width=8) (actual time=97.146..20274.526 rows=16067 loops=1)
                    ->  Bitmap Heap Scan on weekly_baskets  (cost=26501.11..62382.74 rows=33112 width=8) (actual time=97.049..15395.178 rows=34502 loops=1)
                          Recheck Cond: (menu_id = 344)
                          Heap Blocks: exact=12414
                          ->  Bitmap Index Scan on index_weekly_baskets_on_user_id_and_menu_id  (cost=0.00..26499.45 rows=33112 width=0) (actual time=94.135..94.135 rows=34514 loops=1)
                                Index Cond: (menu_id = 344)
                    ->  Index Only Scan using uidx_cancellations_for_active on cancellations  (cost=0.08..0.77 rows=1 width=4) (actual time=0.141..0.141 rows=1 loops=34502)
                          Index Cond: ((cancellable_id = weekly_baskets.id) AND (cancellable_type = 'WeeklyBasket'::text) AND (active = true))
                          Heap Fetches: 12907
              ->  Index Only Scan using weekly_baskets_pkey on weekly_baskets weekly_baskets_meal_selections  (cost=0.09..1.61 rows=1 width=4) (actual time=0.004..0.010 rows=1 loops=16067)
                    Index Cond: (id = weekly_baskets.id)
                    Heap Fetches: 16595
        ->  Index Only Scan using index_meal_selections_on_weekly_basket_id_and_meal_id on meal_selections  (cost=0.09..0.14 rows=4 width=8) (actual time=0.508..0.737 rows=3 loops=16067)
              Index Cond: (weekly_basket_id = weekly_baskets_meal_selections.id)
              Heap Fetches: 47391
Planning time: 1.568 ms
Execution time: 32313.715 ms

显然这很痛苦。我很难有可能使用LATERAL联接来优化此查询,但无法绕过它。

1 个答案:

答案 0 :(得分:1)

尝试将您的子选择更改为左连接,并将其检查为空,这应该有所帮助。

SELECT
   SUM(weekly_baskets.servings / 2) AS sum_weekly_baskets_servings_2,
   meal_selections.meal_id AS meal_selections_meal_id 
FROM
   "meal_selections" 
INNER JOIN
   "weekly_baskets" "weekly_baskets_meal_selections" 
      ON "weekly_baskets_meal_selections"."id" = "meal_selections"."weekly_basket_id" 
INNER JOIN
   "weekly_baskets" 
      ON "meal_selections"."weekly_basket_id" = "weekly_baskets"."id" 
LEFT JOIN "cancellations" on 
         "cancellations"."cancellable_id" = "weekly_baskets"."id"
         AND "cancellations"."cancellable_type" = 'WeeklyBasket' 
         AND "cancellations"."active" = 't'
WHERE
   "weekly_baskets"."menu_id" = 344 
   AND "cancellations"."cancellable_id" is null
GROUP BY
   meal_selections.meal_id