真正长时间运行查询时使用order by

mar*_*rek 1 postgresql

我的一个查询存在一个主要问题:

SELECT tpostime, gispoint 
FROM mytable 
WHERE idterminal = 233463 
ORDER BY idpos DESC
Run Code Online (Sandbox Code Playgroud)

当'mytable'中不存在idterminal时,此查询将被永久处理,然后我会看到超时(由于用户请求'消息声明是特定的),但是当我删除order by子句时,一切似乎都很好.现在我想知道 - idpos是'mytable'的主键,因此它被索引,所以它的排序应该很快,我猜.重要的是 - 'mytable'重量为3gb.

表和索引定义:

CREATE TABLE mytable ( 
  idpos serial NOT NULL, 
  tpostime timestamp(0) without time zone, 
  idterminal integer DEFAULT 0, 
  gispoint geometry, 
  idtracks integer, 
  CONSTRAINT mytable_pkey PRIMARY KEY (idpos), 
  CONSTRAINT qwe FOREIGN KEY (idtracks) REFERENCES qwe (idtracks) 
    MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE, 
  CONSTRAINT abc FOREIGN KEY (idterminal) REFERENCES abc (idterminal)
    MATCH SIMPLE ON UPDATE NO ACTION ON DELETE CASCADE, 
  CONSTRAINT enforce_geotype_gispoint 
    CHECK (geometrytype(gispoint)= 'POINT'::text OR gispoint IS NULL), 
  CONSTRAINT enforce_srid_gispoint CHECK (srid(gispoint) = 4326)
) WITH OIDS; 

CREATE INDEX idx_idterminal ON mytable USING btree (idterminal); 
CREATE INDEX idx_idtracks ON mytable USING btree (idtracks); 
CREATE INDEX idx_idtracks_idterminal ON mytable USING btree (idtracks, idterminal);
Run Code Online (Sandbox Code Playgroud)

小智 5

在我看来,idterminal的选择性足够低,以便postgres选择全扫描mytable_pkey而不是订购所有行的成本idterminal = 233463

我建议:

CREATE INDEX idx_idterminal2 ON mytable USING btree (idterminal, idpos);
Run Code Online (Sandbox Code Playgroud)

也许:

DROP INDEX idx_idterminal;
Run Code Online (Sandbox Code Playgroud)

您没有提及这是否是生产数据库 - 如果您当然需要先在其他地方测试更改的影响.

如果您不想更改架构,您可能会尝试将优化器引入您认为最适合8.4(及以上)的(未经测试)的路径:

SELECT * 
FROM ( SELECT tpostime, gispoint, idpos, row_number() over (order by 1)
       FROM mytable 
       WHERE idterminal = 233463 )
ORDER BY idpos DESC;
Run Code Online (Sandbox Code Playgroud)

或者只是:

SELECT * 
FROM ( SELECT tpostime, gispoint, idpos
       FROM mytable 
       WHERE idterminal = 233463
       GROUP BY tpostime, gispoint, idpos )
ORDER BY idpos DESC;
Run Code Online (Sandbox Code Playgroud)

甚至:

SELECT tpostime, gispoint 
FROM mytable 
WHERE idterminal = 233463 
ORDER BY idpos*2 DESC
Run Code Online (Sandbox Code Playgroud)