我对某些时间序列数据有一个简单的选择:
SELECT DISTINCT user_id
FROM events
WHERE project_id = 6
AND time > '2015-01-11 8:00:00'
AND time < '2015-02-10 8:00:00';
而且需要112秒。这是查询计划:
http://explain.depesz.com/s/NTyA
我的应用程序必须执行许多不同的操作,并像这样计数。有没有更快的方法来获取此类数据?
我对某些时间序列数据有一个简单的选择:
SELECT DISTINCT user_id
FROM events
WHERE project_id = 6
AND time > '2015-01-11 8:00:00'
AND time < '2015-02-10 8:00:00';
而且需要112秒。这是查询计划:
http://explain.depesz.com/s/NTyA
我的应用程序必须执行许多不同的操作,并像这样计数。有没有更快的方法来获取此类数据?
Answers:
您可能不想听到此消息,但是加快速度的最佳选择SELECT DISTINCT
是避免 DISTINCT
从头开始。在许多情况下(不是全部!),可以通过更好的数据库设计或更好的查询来避免这种情况。
有时,GROUP BY
速度更快,因为它采用了不同的代码路径。
在您的具体情况下,似乎无法摆脱DISTINCT
。但是,如果您有许多此类查询,则可以使用专门的索引来支持查询:
CREATE INDEX foo ON events (project_id, "time", user_id);
user_id
仅当您从中获得仅索引扫描时,添加才有用。点击链接获取详细信息。从您的查询计划中删除昂贵的位图堆扫描,这将占用90%的查询时间。
您的EXPLAIN
输出告诉我,查询必须压缩半百万个匹配行中的2,491个不同的用户。无论您做什么,这都不会变得很快,但是它可能会更快。
如果查询中的时间间隔始终相同,则每MATERIALIIZED VIEW
折叠user_id
一次(project_id, <fixed time intervall>)
将有很大帮助。但是,在不同的时间间隔内没有机会。也许您每小时至少可以折回一个用户或其他一些最小时间单位,这将购买足够的性能以保证可观的开销。
Nitpick:
最有可能的谓词"time"
应该确实是:
AND "time" >= '2015-01-11 8:00:00'
AND "time" < '2015-02-10 8:00:00';
除了:
不要time
用作标识符。它是标准SQL中的保留字,而在Postgres中是基本类型。
这是我对山姆案和欧文案答案的检验
drop table t1
create table t1 (id int, user_id int, project_id int, date_time timestamp without time zone) ;
insert into t1 -- 10 million row - size="498 MB"
select row_number() over(), round(row_number() over()/1000), round(row_number() over()/100000) , date
from generate_series('2015-01-01'::date, '2016-12-01'::date,'6 seconds'::interval
) date
limit 10000000
-- before indexing - 10000000 row - output=100 row - time=2900ms
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 8:00:00'
AND date_time < '2016-12-01 8:00:00' ;
CREATE INDEX foo ON t1 (project_id, date_time, user_id); -- time process=51.2 secs -- size="387 MB"
-- after indexing - 10000000 row - output=100 row - time= 75ms (reduce ~ 38 times)
SELECT DISTINCT user_id
FROM t1
WHERE project_id = 1
AND date_time > '2015-01-01 00:00:00'
AND date_time < '2016-12-01 00:00:00' ;
欧文说:“您可能不希望听到此消息,但是加快SELECT DISTINCT的最佳选择是避免以DISTINCT开头。在许多情况下(并非全部!),可以通过更好的数据库设计或更好的查询来避免“。我认为他是对的,我们应该避免使用“区别,分组,排序”(如果有)。
我遇到了Sam的情况,我认为Sam可以按月在事件表上使用分区。查询时会减少数据量,但是需要一个函数(pl / pgsql)来执行,而不是上面的查询。该函数将找到适当的分区(取决于条件)以执行query。