在查询中使用EXPLAIN,我应该从哪里开始寻找优化?

时间:2017-07-30 19:38:07

标签: postgresql

我有一个使用512MB work_mem在postgres中花费3个小时的查询。

查询:

            SELECT
                nextval(
                    'rpro_vistas_hd.seq_ventas_inventario_cod'
                ) AS COD,
                MAX (tmpsem.fecha) AS fecha_inventario,
                MAX (inve.inventario_monto) AS MONTO_INVENTARIO,
                MAX (inve.inventario_unidades) AS "UNIDADES_INVENTARIO",
                CASE
            WHEN SUM (venta.monto_venta) IS NULL THEN
                0
            ELSE
                CAST (
                    SUM (venta.monto_venta) AS BIGINT
                )
            END AS "MONTO_VENTA",
             CASE
            WHEN SUM (venta.unidad_venta) IS NULL THEN
                0
            ELSE
                CAST (
                    SUM (venta.unidad_venta) AS BIGINT
                )
            END AS "UNIDADES_VENTA",
             ll.cod_local AS "COD_LOCAL",
             prod.cod_plu AS "COD_PLU_PRODUCTO",
             prod.cod_plu_retail AS "COD_PLU_RETAIL",
             9999 :: NUMERIC AS "COD_PERIODO",
             to_char(- 1, '99') AS historial_alerta,
             CASE
            WHEN SUM (venta.unidad_venta / 7) > 0 THEN
                CASE
            WHEN (
                MAX (inve.inventario_unidades) / SUM (venta.unidad_venta / 7)
            ) IS NULL THEN
                0
            ELSE
                CAST (
                    MAX (inve.inventario_unidades) / SUM (venta.unidad_venta / 7) AS INTEGER
                )
            END
            ELSE
                0
            END AS rotacion_inventario,
             CASE
            WHEN (SUM(venta.unidad_venta) > 0) THEN
                (
                    SUM (venta.monto_venta) / SUM (venta.unidad_venta)
                ) * 1.19
            ELSE
                ((- 999999)) :: INTEGER
            END AS precio_promedio,
             bool_or(habi.habilitado_retailer) AS "HABILITADO_LOCAL",
             SUM (
                res.monto_ventas_ultimo_anio_movil
            ) AS "MONTO_VENTAS_ULTIMO_ANIO_MOVIL",
             CASE
            WHEN MAX (opp.oportunidad_mo) IS NULL THEN
                0
            ELSE
                MAX (opp.oportunidad_mo)
            END AS oportunidad_monto,
             CASE
            WHEN MAX (opp.oportunidad_un) IS NULL THEN
                0
            ELSE
                MAX (opp.oportunidad_un)
            END AS oportunidad_inventario
            FROM
                rpro_vistas_hd.perfil_1_clr_ultimo_inventario inve
            INNER JOIN rpro_dim.dim_locales ll ON inve.cod_local = ll.cod_local
            INNER JOIN rpro_dim.dim_productos_retail prod ON prod.cod_plu_retail = inve.cod_plu_retail
            AND inve.cod_plu = prod.cod_plu
            AND ll.cod_retailer = prod.cod_retailer
            AND ll.cod_cadena = prod.cod_cadena
            JOIN rpro_vistas_hd.perfil_1_clr_periodo_diario_y_semanal AS tmpsem ON tmpsem.fecha = inve.fecha_inventario
            AND ll.cod_cadena = tmpsem.cod_cadena
            LEFT JOIN rpro_vistas_hd.perfil_1_clr_ultima_oportunidad opp ON opp.cod_local = inve.cod_local
            AND opp.cod_plu = inve.cod_plu
            AND opp.cod_plu_retail = inve.cod_plu_retail
            LEFT JOIN rpro_admin.rms_habilitacion AS habi ON habi.cod_plu_retail = prod.cod_plu_retail
            AND habi.cod_plu = prod.cod_plu
            AND habi.cod_local = ll.cod_local
            LEFT JOIN rpro_vistas_hd.perfil_1_clr_resumen_producto_por_local_full AS res ON res.cod_local = ll.cod_local
            AND res.cod_plu_retail = prod.cod_plu_retail
            AND res.cod_plu_producto = prod.cod_plu
            LEFT JOIN (
                SELECT
                    vvdd.id_dim_local,
                    vvdd.id_dim_prod_retail,
                    SUM (
                        vvdd.sellout_dia_actual_mon
                    ) AS monto_venta,
                    SUM (
                        vvdd.sellout_dia_actual_uni
                    ) AS unidad_venta
                FROM
                    rpro_dim.dim_ventas_diarias AS vvdd
                JOIN rpro_dim.dim_tiempo_diario AS tmp ON tmp.id_dim_tiempo_diario = vvdd.id_dim_tiempo_diario
                JOIN rpro_dim.dim_locales ll ON vvdd.id_dim_local = ll.id_dim_local
                JOIN rpro_dim.dim_productos_retail pp ON vvdd.id_dim_prod_retail = pp.id_dim_prod_retail
                RIGHT JOIN rpro_vistas_hd.perfil_1_clr_periodo_diario_y_semanal tmpsem ON vvdd.id_dim_tiempo_diario = tmpsem.id_dim_tiempo_diario
                AND ll.cod_cadena = tmpsem.cod_cadena
                GROUP BY
                    vvdd.id_dim_local,
                    vvdd.id_dim_prod_retail
            ) AS venta ON venta.id_dim_local = ll.id_dim_local
            AND venta.id_dim_prod_retail = prod.id_dim_prod_retail
            AND prod.cod_plu != 'NOCORRESPONDE'
            GROUP BY
                prod.cod_plu,
                ll.cod_local,
                prod.cod_plu_retail
            ORDER BY
                ll.cod_local,
                prod.cod_plu_retail;

当我对此查询执行EXPLAIN时,我得到了这个:

GroupAggregate  (cost=694653.37..706072.47 rows=1 width=1515)
Group Key: ll.cod_local, prod.cod_plu_retail, prod.cod_plu
->  Nested Loop Left Join  (cost=694653.37..706072.34 rows=1 width=1451)
    Join Filter: ((res.cod_local)::text = (ll.cod_local)::text)
    ->  Nested Loop Left Join  (cost=694652.95..706038.43 rows=1 width=1447)
          Join Filter: (((opp.cod_local)::text = (inve.cod_local)::text) AND ((opp.cod_plu)::text = (inve.cod_plu)::text) AND ((opp.cod_plu_retail)::text = (inve.cod_plu_retail)::text))
          ->  Merge Join  (cost=694652.95..696760.40 rows=1 width=2633)
                Merge Cond: (((ll.cod_local)::text = (inve.cod_local)::text) AND ((prod.cod_plu_retail)::text = (inve.cod_plu_retail)::text) AND ((prod.cod_plu)::text = (inve.cod_plu)::text))
                Join Filter: (tmpsem.cod_cadena = ll.cod_cadena)
                ->  Merge Left Join  (cost=662750.31..662799.00 rows=4808 width=1323)
                      Merge Cond: (((ll.cod_local)::text = (habi.cod_local)::text) AND ((prod.cod_plu_retail)::text = (habi.cod_plu_retail)::text) AND ((prod.cod_plu)::text = (habi.cod_plu)::text))
                      ->  Sort  (cost=662737.93..662749.95 rows=4808 width=1322)
                            Sort Key: ll.cod_local, prod.cod_plu_retail, prod.cod_plu
                            ->  Hash Right Join  (cost=661432.51..662443.90 rows=4808 width=1322)
                                  Hash Cond: ((vvdd.id_dim_local = ll.id_dim_local) AND (vvdd.id_dim_prod_retail = prod.id_dim_prod_retail))
                                  Join Filter: ((prod.cod_plu)::text <> 'NOCORRESPONDE'::text)
                                  ->  GroupAggregate  (cost=652944.88..653562.94 rows=22475 width=72)
                                        Group Key: vvdd.id_dim_local, vvdd.id_dim_prod_retail
                                        ->  Sort  (cost=652944.88..653001.07 rows=22475 width=48)
                                              Sort Key: vvdd.id_dim_local, vvdd.id_dim_prod_retail
                                              ->  Nested Loop Left Join  (cost=1307.72..651320.38 rows=22475 width=48)
                                                    ->  Seq Scan on perfil_1_clr_periodo_diario_y_semanal tmpsem_1  (cost=0.00..1.84 rows=84 width=8)
                                                    ->  Nested Loop  (cost=1307.72..7751.11 rows=268 width=56)
                                                          ->  Nested Loop  (cost=1307.43..7665.71 rows=268 width=56)
                                                                ->  Hash Join  (cost=1307.15..7583.30 rows=268 width=56)
                                                                      Hash Cond: (vvdd.id_dim_local = ll_1.id_dim_local)
                                                                      Join Filter: (ll_1.cod_cadena = tmpsem_1.cod_cadena)
                                                                      ->  Bitmap Heap Scan on dim_ventas_diarias vvdd  (cost=900.95..6307.55 rows=53511 width=52)
                                                                            Recheck Cond: (id_dim_tiempo_diario = tmpsem_1.id_dim_tiempo_diario)
                                                                            ->  Bitmap Index Scan on dim_ventas_diarias_id_dim_tiempo_diario_idx  (cost=0.00..887.58 rows=53511 width=0)
                                                                                  Index Cond: (id_dim_tiempo_diario = tmpsem_1.id_dim_tiempo_diario)
                                                                      ->  Hash  (cost=358.31..358.31 rows=3831 width=8)
                                                                            ->  Seq Scan on dim_locales ll_1  (cost=0.00..358.31 rows=3831 width=8)
                                                                ->  Index Only Scan using dim_tiempo_diario_pkey on dim_tiempo_diario tmp  (cost=0.28..0.30 rows=1 width=4)
                                                                      Index Cond: (id_dim_tiempo_diario = vvdd.id_dim_tiempo_diario)
                                                          ->  Index Only Scan using id_dim_prod_retail_hist_pkey2 on dim_productos_retail pp  (cost=0.29..0.31 rows=1 width=4)
                                                                Index Cond: (id_dim_prod_retail = vvdd.id_dim_prod_retail)
                                  ->  Hash  (cost=8415.51..8415.51 rows=4808 width=1266)
                                        ->  Merge Join  (cost=7962.17..8415.51 rows=4808 width=1266)
                                              Merge Cond: ((ll.cod_cadena = prod.cod_cadena) AND (ll.cod_retailer = prod.cod_retailer))
                                              ->  Sort  (cost=586.32..595.90 rows=3831 width=230)
                                                    Sort Key: ll.cod_cadena, ll.cod_retailer
                                                    ->  Seq Scan on dim_locales ll  (cost=0.00..358.31 rows=3831 width=230)
                                              ->  Sort  (cost=7375.85..7501.36 rows=50204 width=1044)
                                                    Sort Key: prod.cod_cadena, prod.cod_retailer
                                                    ->  Seq Scan on dim_productos_retail prod  (cost=0.00..3456.04 rows=50204 width=1044)
                      ->  Sort  (cost=12.37..12.52 rows=60 width=1251)
                            Sort Key: habi.cod_local, habi.cod_plu_retail, habi.cod_plu
                            ->  Seq Scan on rms_habilitacion habi  (cost=0.00..10.60 rows=60 width=1251)
                ->  Sort  (cost=31902.64..32407.94 rows=202119 width=1322)
                      Sort Key: inve.cod_local, inve.cod_plu_retail, inve.cod_plu
                      ->  Hash Join  (cost=2.89..14091.06 rows=202119 width=1322)
                            Hash Cond: (inve.fecha_inventario = tmpsem.fecha)
                            ->  Seq Scan on perfil_1_clr_ultimo_inventario inve  (cost=0.00..10262.35 rows=481235 width=1318)
                            ->  Hash  (cost=1.84..1.84 rows=84 width=8)
                                  ->  Seq Scan on perfil_1_clr_periodo_diario_y_semanal tmpsem  (cost=0.00..1.84 rows=84 width=8)
          ->  Seq Scan on perfil_1_clr_ultima_oportunidad opp  (cost=0.00..4880.10 rows=251310 width=1612)
    ->  Index Scan using perfil_1_clr_resumen_producto_por_local_fu_cod_plu_producto_idx on perfil_1_clr_resumen_producto_por_local_full res  (cost=0.42..33.82 rows=8 width=1254)
          Index Cond: ((cod_plu_producto)::text = (prod.cod_plu)::text)
          Filter: ((cod_plu_retail)::text = (prod.cod_plu_retail)::text)

鉴于此查询计划,我应该从哪里开始进行优化?我正在考虑为此添加索引:

(((opp.cod_local)::text = (inve.cod_local)::text) AND ((opp.cod_plu)::text = (inve.cod_plu)::text) AND ((opp.cod_plu_retail)::text = (inve.cod_plu_retail)::text))

任何提示都非常感谢。很多这样的表没有索引,这就是为什么我需要知道使用EXPLAIN并使用不同的比较器将索引应用于列是一个好的起点。或者也许应该努力优化查询?

1 个答案:

答案 0 :(得分:1)

您的问题是您的子查询(别名为venta)引用表rpro_vistas_hd,这意味着它是correlated subquery,这反过来意味着它被执行前面连接的每一行

要解决这个问题,你就是正确地加入了外表,而且正确的连接表现得很糟糕。

重写您的查询以不使用相关子查询。