仅4个表且每个表有50000行的查询速度太慢

时间:2018-12-04 20:32:54

标签: postgresql postgresql-performance

我已经奋斗了几个小时,却找不到为什么这个查询花费太长时间(> 60分钟)的原因。所有4个表的记录少于50.000。

此外,如果我删除任何表(gel6,gf6或ger6),则查询只需不到500毫秒即可执行。我究竟做错了什么?

说明计划: https://explain.depesz.com/s/ldm2

 SELECT COUNT(*)
 FROM agroapp.ganado g
 INNER JOIN (SELECT gel5.ganado_id, gel5.estado_leche  
             FROM agroapp.ganado_estado_leche gel5  
             INNER JOIN (SELECT MAX(gel3.ganado_estado_leche_id) ganado_estado_leche_id  
                         FROM agroapp.ganado_estado_leche gel3  
                         INNER JOIN (SELECT gel.ganado_id, MAX(gel.created) created  
                                     FROM agroapp.ganado_estado_leche gel   
                                     GROUP BY gel.ganado_id) gel2 ON (gel2.ganado_id = gel3.ganado_id AND gel2.created = gel3.created)  
                         GROUP BY gel3.ganado_id) gel4 ON gel4.ganado_estado_leche_id = gel5.ganado_estado_leche_id  
            ) gel6 ON gel6.ganado_id = g.ganado_id
 INNER JOIN (SELECT gf5.ganado_id, gf5.fundo_id  
             FROM agroapp.ganado_fundo gf5  
             INNER JOIN (SELECT MAX(gf3.ganado_fundo_id) ganado_fundo_id  
                         FROM agroapp.ganado_fundo gf3  
                         INNER JOIN (SELECT gf.ganado_id, MAX(gf.created) created  
                                     FROM agroapp.ganado_fundo gf  
                                     GROUP BY gf.ganado_id) gf2 ON (gf2.ganado_id = gf3.ganado_id AND gf2.created = gf3.created)    
                         GROUP BY gf3.ganado_id) gf4 ON gf4.ganado_fundo_id = gf5.ganado_fundo_id  
            ) gf6 ON gf6.ganado_id = g.ganado_id
 INNER JOIN (SELECT ger5.ganado_id, ger5.estado_reproductivo  
             FROM agroapp.ganado_estado_reproductivo ger5  
             INNER JOIN (SELECT MAX(ger3.ganado_estado_reproductivo_id) ganado_estado_reproductivo_id  
                         FROM agroapp.ganado_estado_reproductivo ger3  
                         INNER JOIN (SELECT ger.ganado_id, MAX(ger.created) created  
                                     FROM agroapp.ganado_estado_reproductivo ger  
                                     GROUP BY ger.ganado_id) ger2 ON (ger2.ganado_id = ger3.ganado_id AND ger2.created = ger3.created)  
                         GROUP BY ger3.ganado_id) ger4 ON ger4.ganado_estado_reproductivo_id = ger5.ganado_estado_reproductivo_id  
            ) ger6 ON ger6.ganado_id = g.ganado_id
WHERE g.organizacion_id = 21

表格

CREATE TABLE agroapp.ganado_estado_leche
(
  ganado_estado_leche_id serial NOT NULL,
  organizacion_id integer NOT NULL,
  isactive character(1) NOT NULL DEFAULT 'Y'::bpchar,
  created timestamp without time zone NOT NULL DEFAULT now(),
  createdby numeric(10,0) NOT NULL,
  updated timestamp without time zone NOT NULL DEFAULT now(),
  updatedby numeric(10,0) NOT NULL,
  estado_leche character varying(80) NOT NULL,
  ganado_id integer NOT NULL,
  fecha_manejo timestamp without time zone NOT NULL,
  CONSTRAINT ganado_estado_leche_pk PRIMARY KEY (ganado_estado_leche_id),
  CONSTRAINT ganado_fk FOREIGN KEY (ganado_id)
      REFERENCES agroapp.ganado (ganado_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)

CREATE TABLE agroapp.ganado_fundo
(
  ganado_fundo_id serial NOT NULL,
  organizacion_id integer NOT NULL,
  isactive character(1) NOT NULL DEFAULT 'Y'::bpchar,
  created timestamp without time zone NOT NULL DEFAULT now(),
  createdby numeric(10,0) NOT NULL,
  updated timestamp without time zone NOT NULL DEFAULT now(),
  updatedby numeric(10,0) NOT NULL,
  fundo_id integer NOT NULL,
  ganado_id integer NOT NULL,
  CONSTRAINT ganado_fundo_pk PRIMARY KEY (ganado_fundo_id),
  CONSTRAINT ganado_fk FOREIGN KEY (ganado_id)
      REFERENCES agroapp.ganado (ganado_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)

CREATE TABLE agroapp.ganado_estado_reproductivo
(
  ganado_estado_reproductivo_id serial NOT NULL,
  organizacion_id integer NOT NULL,
  isactive character(1) NOT NULL DEFAULT 'Y'::bpchar,
  created timestamp without time zone NOT NULL DEFAULT now(),
  createdby numeric(10,0) NOT NULL,
  updated timestamp without time zone NOT NULL DEFAULT now(),
  updatedby numeric(10,0) NOT NULL,
  estado_reproductivo character varying(80) NOT NULL,
  ganado_id integer NOT NULL,
  fecha_manejo timestamp without time zone NOT NULL,
  CONSTRAINT ganado_estado_reproductivo_pk PRIMARY KEY (ganado_estado_reproductivo_id),
  CONSTRAINT ganado_fk FOREIGN KEY (ganado_id)
      REFERENCES agroapp.ganado (ganado_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)

CREATE TABLE agroapp.ganado
(
  ganado_id serial NOT NULL,
  organizacion_id integer NOT NULL,
  isactive character(1) NOT NULL DEFAULT 'Y'::bpchar,
  created timestamp without time zone NOT NULL DEFAULT now(),
  createdby numeric(10,0) NOT NULL,
  updated timestamp without time zone NOT NULL DEFAULT now(),
  updatedby numeric(10,0) NOT NULL,
  fecha_nacimiento timestamp without time zone NOT NULL,
  tipo_ganado character varying(80) NOT NULL,
  diio_id integer NOT NULL,
  fundo_id integer NOT NULL,
  raza_id integer NOT NULL,
  estado_reproductivo character varying(80) NOT NULL,
  estado_leche character varying(80),
  CONSTRAINT ganado_pk PRIMARY KEY (ganado_id),
  CONSTRAINT diio_fk FOREIGN KEY (diio_id)
      REFERENCES agroapp.diio (diio_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT fundo_fk FOREIGN KEY (fundo_id)
      REFERENCES agroapp.fundo (fundo_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION,
  CONSTRAINT raza_fk FOREIGN KEY (raza_id)
      REFERENCES agroapp.raza (raza_id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE NO ACTION
)

1 个答案:

答案 0 :(得分:0)

表格设计

  • 这看起来很像一个boolean列(是/否):

    isactive character(1) NOT NULL DEFAULT 'Y'::bpchar
    

    如果是这样,请替换为:

    isactive bool NOT NULL DEFAULT TRUE
    
  • 如果您可能以任何方式涉及多个时区,请在此处使用timestamptz代替timestamp

    created timestamp without time zone NOT NULL DEFAULT now(),
    

默认的now()产生timestamptz,并且在分配转换后,根据会话的时区得出当前时间。也就是说,该值随会话的timezone而变化,这是失败的隐秘点。参见:
 -Ignoring time zones altogether in Rails and PostgreSQL

并且:

createdby numeric(10,0) NOT NULL
等。看起来他们真的应该只是integer。 (或者bigint,如果您真的认为您可能会烧掉超过2147483648个数字...)

查询

查看第一个子查询:

 SELECT gel5.ganado_id, gel5.estado_leche  
 FROM agroapp.ganado_estado_leche gel5  
 INNER JOIN (
    SELECT MAX(gel3.ganado_estado_leche_id) ganado_estado_leche_id  
    FROM agroapp.ganado_estado_leche gel3  
    INNER JOIN (
       SELECT gel.ganado_id, MAX(gel.created) created  
       FROM agroapp.ganado_estado_leche gel   
       GROUP BY gel.ganado_id
       ) gel2 ON (gel2.ganado_id = gel3.ganado_id AND gel2.created = gel3.created)  
    GROUP BY gel3.ganado_id
    ) gel4 ON gel4.ganado_estado_leche_id = gel5.ganado_estado_leche_id

最里面的子查询获得最大值。每created ganado_id,下一个是这些行的最大ganado_estado_leche_id。最后,您重新加入并检索所有ganado_id并与每个分区确定的最大ganado_estado_leche_id一起出现。我很难理解这一点,但是可以简化为:

SELECT gel2.ganado_id
FROM   agroapp.ganado_estado_leche gel2
JOIN  (
   SELECT DISTINCT ON (ganado_id) ganado_estado_leche_id
   FROM   agroapp.ganado_estado_leche
   ORDER  BY ganado_id, created DESC NULLS LAST, ganado_estado_leche_id DESC NULLS LAST
   ) gel1 USING (ganado_estado_leche_id)

请参阅:

对我来说好像是一个不正确的查询。与查询的其余部分相同:联接以奇怪的方式乘以行。不知道您要计算什么,但我怀疑查询是否仅此而已。您没有提供足够的信息来理解它。