使用示例表:
create table user_login (
user_id integer not null,
login_time numeric not null, -- seconds since epoch or similar
constraint unique(user_id, login_time)
);
create table user_page_visited (
page_id integer not null,
page_visited_at numeric not null -- seconds since epoch or similar
);
示例数据:
> user_login
user_id login_time
1 1 100
2 1 140
> user_page_visited
page_id page_visited_at
1 1 100
2 1 200
3 2 120
4 2 130
5 3 160
6 3 150
我希望返回基于user_page_visited
的所有user_login.login_time
行,例如,返回在现有login_time的20秒内访问的所有页面:
> user_page_visited
page_id page_visited_at
1 1 100
3 2 120
5 3 160
6 3 150
当两个表都有很多行时,如何有效地执行此操作?例如,以下查询执行类似的操作(当范围重叠时返回重复的行),但似乎非常慢:
select * from
user_login l cross join
user_page_visited v
where v.page_visited_at >= l.login_time
and v.page_visited_at <= l.login_time + 20;
答案 0 :(得分:1)
首先,使用常规join
语法:
select *
from user_login l join
user_page_visited v
on v.page_visited_at >= l.login_time and
v.page_visited_at <= l.login_time + 20;
接下来,请确保您在用于join
的列上有索引。 。 。 user_login(login_time)
和user_page_visited(page_visited_at)
。
如果这些不起作用,那么你还有几个选择。如果&#34; 20&#34;固定,您可以改变索引的类型。如果您只是在登录和访问的页面之间寻找一个匹配项,那么也有一些技巧。
答案 1 :(得分:1)
此解决方案基于Gordon Linoff答案的评论。
首先,我们使用以下查询检索在与用户连接相同的时间片中访问的元组,或者在以下时间片中检索:
SELECT DISTINCT page_id, page_visited_at
FROM user_login
INNER JOIN user_page_visited ON login_time::INT / 20 = page_visited_at::INT / 20 OR login_time::INT / 20 = page_visited_at::INT / 20 - 1;
我们现在需要索引才能获得一个好的查询计划:
CREATE INDEX i_user_login_login_time_20 ON user_login ((login_time::INT / 20));
CREATE INDEX i_user_page_visited_page_visited_at_20 ON user_page_visited ((page_visited_at::INT / 20));
CREATE INDEX i_user_page_visited_page_visited_at_20_minus_1 ON user_page_visited ((page_visited_at::INT / 20 - 1));
如果使用这些索引探测查询,则会在两个位图索引扫描操作中获得一个BitmapOr,并且具有一些低常量成本。另一方面,如果没有这些索引,您会以更高的成本获得顺序扫描(我使用每个~100k元组的表进行测试)。
然而,此查询会产生太多结果。我们需要再次过滤它以获得最终结果:
SELECT DISTINCT page_id, page_visited_at
FROM user_login
INNER JOIN user_page_visited ON login_time::INT / 20 = page_visited_at::INT / 20 OR login_time::INT / 20 = page_visited_at::INT / 20 - 1
WHERE page_visited_at BETWEEN login_time AND login_time + 20;
在此查询上使用EXPLAIN显示PostgreSQL仍然使用位图索引扫描。
在user_login中有大约100k行,在user_page_visited中大约有200k行,查询需要~1.4s来检索~200k行而没有切片预过滤器需要3.5s。 (uname -a:Linux shepwork 4.4.26-gentoo#8 SMP Mon Nov 21 09:45:10 CET 2016 x86_64 AMD FX(tm)-6300六核处理器AuthenticAMD GNU / Linux)