【问题标题】:Query taking too long查询时间过长
【发布时间】:2017-07-20 00:12:09
【问题描述】:

我正在一个简单的 SCADA 系统中开发 PostgreSQL(最新版本)。问题是每个涉及我架构中最大表的查询 [图 1] 大约需要 2 小时。

我想要的是获得建筑物 (edificio) 的构造函数的 PK,其部门 (departamento) 记录 (valor 在表 medicion) 在“gas”或“electricidad”测量中的盈余2010. 所以...

  • variable 包含记录的最大值 (valmax)
  • medicion 包含指标的值 (valor)

表格大小(以行为单位)。

  • constructura -> 10
  • edificio -> 100
  • departamento -> 50.000
  • variable -> 8
  • medicion_departamento -> 400.000
  • medicion -> 8.000.000

查询

我使用INNER JOINs 进行了查询,从最小的表(constructoravariable)到最大的表(medicion)。

SELECT DISTINCT C.id_constructora FROM
constructora C
INNER JOIN variable V ON (V.nombre = 'electricidad' OR V.nombre = 'gas')
INNER JOIN edificio E ON (E.id_constructora = C.id_constructora)
INNER JOIN departamento D ON (E.id_edificio = D.id_edificio)
INNER JOIN medicion M ON (M.id_variable = V.id_variable)
WHERE (
    (M.valor > V.valmax) AND
    EXTRACT(YEAR FROM M.fecha) = 2010
);

解释

"HashAggregate  (cost=2343438.58..2343438.68 rows=10 width=4)"
"  Group Key: c.id_constructora"
"  ->  Hash Join  (cost=164536.25..1947605.25 rows=158333333 width=4)"
"        Hash Cond: (e.id_constructora = c.id_constructora)"
"        ->  Hash Join  (cost=4.25..1510.75 rows=50000 width=4)"
"              Hash Cond: (d.id_edificio = e.id_edificio)"
"              ->  Seq Scan on departamento d  (cost=0.00..819.00 rows=50000 width=4)"
"              ->  Hash  (cost=3.00..3.00 rows=100 width=8)"
"                    ->  Seq Scan on edificio e  (cost=0.00..3.00 rows=100 width=8)"
"        ->  Hash  (cost=164136.12..164136.12 rows=31670 width=4)"
"              ->  Nested Loop  (cost=0.00..164136.12 rows=31670 width=4)"
"                    ->  Nested Loop  (cost=0.00..163739.12 rows=3167 width=0)"
"                          Join Filter: ((m.valor > v.valmax) AND (v.id_variable = m.id_variable))"
"                          ->  Seq Scan on medicion m  (cost=0.00..162408.00 rows=38000 width=8)"
"                                Filter: (date_part('year'::text, fecha) = '2010'::double precision)"
"                          ->  Materialize  (cost=0.00..1.13 rows=2 width=24)"
"                                ->  Seq Scan on variable v  (cost=0.00..1.12 rows=2 width=24)"
"                                      Filter: (((nombre)::text = 'electricidad'::text) OR ((nombre)::text = 'gas'::text))"
"                    ->  Materialize  (cost=0.00..1.15 rows=10 width=4)"
"                          ->  Seq Scan on constructora c  (cost=0.00..1.10 rows=10 width=4)"

我的问题是我可以做些什么来显着减少查询的执行时间?

【问题讨论】:

  • variablemedicion如何与查询中的其他表链接?看来你在里面漏掉了medicion_departamento...
  • 但是我只需要 "valmax" 而不是 medicion_departamento 中的数据,因为我知道 medicion 包含 medicion_departamento 中的 id_variable 并且该表从变量中获取了这个值
  • 但您至少应该使用id_departamento 列将mediciondepartamento 链接起来。不是吗?
  • 这解决了我的问题!现在我可以在 3.2 秒内得到结果。

标签: sql postgresql join query-optimization


【解决方案1】:

看来(根据架构)您的查询应该是:

SELECT DISTINCT C.id_constructora FROM
constructora C
  INNER JOIN edificio E ON (E.id_constructora = C.id_constructora)
  INNER JOIN departamento D ON (E.id_edificio = D.id_edificio)
  -- Changes here
  INNER JOIN medicion M ON (D.id_departamento = M.id_departamento)
  INNER JOIN variable V ON (M.id_variable = V.id_variable AND (V.nombre = 'electricidad' OR V.nombre = 'gas'))
WHERE (
    (M.valor > V.valmax) AND
    EXTRACT(YEAR FROM M.fecha) = 2010
);

因为在您的原始查询中,您有 medicionvariable 表与所有其他表的交叉连接。

你也应该有类似的功能索引

create index idx_medicion_fecha_year on medicion(EXTRACT(YEAR FROM M.fecha));

用于EXTRACT(YEAR FROM fecha) = 2010 条件。

【讨论】:

    【解决方案2】:

    join 中的 or 是性能杀手。也许这可以满足您的要求:

    SELECT DISTINCT C.id_constructora
    FROM constructora C INNER JOIN
         edificio E
         ON E.id_constructora = C.id_constructora INNER JOIN
         departamento D
         ON E.id_edificio = D.id_edificio LEFT JOIN
         variable ve
         ON ve.nombre = 'electricidad' AND M.valor > ve.valmax LEFT JOIN
         variable vg
         ON vg.nombre = 'gas' LEFT JOIN
         medicion me
         ON me.id_variable = ve.id_variable AND
            EXTRACT(YEAR FROM me.fecha) = 2010 AND
             me.valor > ve.valmax LEFT JOIN
         medicion mg
         ON mg.id_variable = vg.id_variable AND
            EXTRACT(YEAR FROM mg.fecha) = 2010 AND
            mg.valor > vg.valmax
    WHERE EXTRACT(YEAR FROM M.fecha) = 2010 AND
          (me.id_variable IS NOT NULL OR mg.id_variable IS NOT NULL);
    

    我还怀疑您希望在适当的 on 子句中加入 mg.id_departamento = d.id_departamentome.id_departamento = d.id_departamento 的条件。

    这假设您在连接键(尤其是主键)上有适当的索引。此外,您还需要variable(nombre, valor)medicion(id_variable, fecha, valor) 上的索引。如果你使用id_departamento,它应该是索引中的第一个键。

    【讨论】:

    • 感谢您的回答!这里的问题是:V.id_variable 是什么?
    • @TomiSebastiánJuárez。 . .我误解了数据模型。我修改了答案。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-02
    • 2019-12-28
    • 2012-03-11
    相关资源
    最近更新 更多