如何改进这个Postgres查询?

时间:2014-07-14 08:21:17

标签: sql postgresql

我已经获得了sql查询:

SELECT cast(AVG(SNR) AS integer) AS snr,
       cast(AVG(RSSI) AS integer) AS rts
FROM SESSION
WHERE DATE(associationtime)>DATE(NOW()- INTERVAL '21 DAYS');

效果很慢,因为21天包含300k行。

 Aggregate  (cost=21768.07..21768.09 rows=1 width=8) (actual time=346.794..346.795 rows=1 loops=1)
   ->  Seq Scan on session  (cost=0.00..20095.77 rows=334459 width=8) (actual time=0.014..282.512 rows=345304 loops=1)
         Filter: (date(associationtime) > date((now() - '21 days'::interval)))
         Rows Removed by Filter: 148508
 Total runtime: 346.867 ms

如何改善查询?我可以创建索引吗?

UPD:

associationtime上的索引没有帮助。

postgres=# CREATE INDEX session_lim_values_idx ON session (associationtime);
CREATE INDEX
postgres=# EXPLAIN (ANALYZE) SELECT cast(AVG(SNR) as integer) as snr, cast(AVG(RSSI) as integer)  as rts FROM session WHERE DATE(associationtime)>DATE(NOW()- INTERVAL '21 DAYS');
                                                      QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=21768.07..21768.09 rows=1 width=8) (actual time=347.654..347.654 rows=1 loops=1)
   ->  Seq Scan on session  (cost=0.00..20095.77 rows=334459 width=8) (actual time=0.014..283.344 rows=345304 loops=1)
         Filter: (date(associationtime) > date((now() - '21 days'::interval)))
         Rows Removed by Filter: 148508
 Total runtime: 347.731 ms

还有DATE(associationtime)

postgres=# CREATE INDEX session_lim_values_idx ON session (DATE(associationtime));
CREATE INDEX
postgres=# EXPLAIN (ANALYZE) SELECT cast(AVG(SNR) as integer) as snr, cast(AVG(RSSI) as integer)  as rts FROM session WHERE DATE(associationtime)>DATE(NOW()- INTERVAL '21 DAYS');
                                                      QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
 Aggregate  (cost=21768.07..21768.09 rows=1 width=8) (actual time=341.050..341.050 rows=1 loops=1)
   ->  Seq Scan on session  (cost=0.00..20095.77 rows=334459 width=8) (actual time=0.015..278.247 rows=345304 loops=1)
         Filter: (date(associationtime) > date((now() - '21 days'::interval)))
         Rows Removed by Filter: 148508
 Total runtime: 341.129 ms

3 个答案:

答案 0 :(得分:3)

由于您只关心整天,您可能希望将结果缓存在物化视图中。

CREATE MATERIALIZED VIEW matview_avg_session
AS SELECT cast(AVG(SNR) AS integer) AS snr,
   cast(AVG(RSSI) AS integer) AS rts
   FROM SESSION
   WHERE DATE(associationtime) > DATE(NOW()- INTERVAL '21 DAYS');
然后

访问这样的数据:

SELECT * FROM matview_avg_session;

并像这样刷新它(每天一次):

REFRESH MATERIALIZED VIEW matview_avg_session;

或者你看一下如何创建触发器来刷新它的答案,但请记住,每次插入后都不想这样做...... Refresh a materialized view automatically using a rule or notify

答案 1 :(得分:1)

您可以使用覆盖索引,即使用仅索引扫描强制执行程序。

对于覆盖索引,您可以先添加在where子句中使用的列,然后在group by中使用列,然后按顺序使用列,然后在select中使用列。

  ALTER TABLE session ADD KEY ix1(date(associationtime), <remaining_columns>);

其中<remaining_columns>是那些,您可以在SFW语句的group by子句中写入。

答案 2 :(得分:1)

associationtime删除日期转换,以便索引能够流行起来。

SELECT cast(AVG(SNR) AS integer) AS snr,
       cast(AVG(RSSI) AS integer) AS rts
FROM SESSION
WHERE associationtime > DATE(NOW() - INTERVAL '20 DAYS');

如果这对VACUUM ANALYZE没有帮助,请再试一次。