Postgres - 慢查询

时间:2018-02-01 21:11:38

标签: sql postgresql

在查询完成执行时遇到一些麻烦 - 它运行并运行并运行,我对索引和查询性能的了解不足以知道如何调整它以加快执行速度。查询如下(它说明了我想要的最终结果):

SELECT
  device.network, device.name AS device, device.mac,
  play.advertiserid, play.filename, play.startdate::timestamp at time zone device.timezone as filestartdate,
  impression.date, impression.views
FROM impression
INNER JOIN device ON
  impression.mac = device.mac
INNER JOIN play ON
  impression.date >= play.startdate::timestamp at time zone device.timezone AND
  impression.date < ((play.startdate::timestamp at time zone device.timezone) + play.spotrunlength * interval '1 second') AND
  play.devicename = device.name
WHERE
  impression.date >= '2017-12-01' AND
  impression.date < '2017-12-31'
ORDER BY impression.date ASC
LIMIT 100;

设备表只有大约100条记录,但展示和播放都有几百万条记录。我在包含所有上述列的所有3个表上创建了索引(包括每个列中的唯一id列),但不确定是否有更好的方法来处理这些索引,或者是否有更好的方法来编写该查询。

2 个答案:

答案 0 :(得分:0)

我不知道您是否有权修改表结构,但如果这样做,您可以尝试在日期列上添加表分区(Postgres 10+)。这应该根据日期加快这些表的加入/搜索条件。

您可以尝试的一些化妆品,可能不会影响查询计划:

  1. 使用impression.DATE BETWEEN '2017-12-01' AND '2017-12-31'运算符表示日期:ROW_NUMBER(OVER ...)

  2. 使用LIMIT 100之类的窗口函数代替CREATE TABLE measurement ( logdate date not null, peaktemp int, unitsales int ) PARTITION BY RANGE (logdate);

  3. <强>更新
    表分区示例(来自手册):

    logdate

    这样您就可以加快指定WHERE logdate BETWEEN XXX AND XXX的范围查询,例如CREATE TABLE measurement_year_month ( logdate date not null, peaktemp int, unitsales int ) PARTITION BY RANGE (EXTRACT(YEAR FROM logdate), EXTRACT(MONTH FROM logdate));

    更复杂的例子(来自手册):

    {{1}}

    要查看的一些链接:

    分区:https://www.postgresql.org/docs/10/static/ddl-partitioning.html
    创建表:https://www.postgresql.org/docs/10/static/sql-createtable.html

答案 1 :(得分:0)

我会说你的数据模型有问题。

加入这样的条件:

impression.date >= play.startdate::timestamp at time zone device.timezone

可能只能使用嵌套循环连接进行处理,并且无法很好地索引条件。

您应该将所有事件存储为timestamp with time zone,而不是存储本地日期和时间戳以及在查询时使用时区进行操作,而不是将其存储为UTC时间戳。

然后您的查询应该变得更加简单,并且可以使用更有效的哈希或合并连接。

可以在您的数据中保留时区信息,但仅将其用于显示目的。

除此之外,请确保impression.date上有索引。