我在 postgres 中有一个具有以下结构的表
表格路径: 乘客、出发地、目的地、日期、月份、年份
我想根据一年内在一条路线上旅行的乘客数量找到前 3 条路线。 一条路线上的乘客总数(A <-> B) = 乘客总数(A -> B) + 乘客总数( B->A )
聚合路线上乘客数量的最佳/最佳方式是什么,表格行数约为 1.5 亿行。
谢谢
最佳答案
有两种方法可以做到这一点。一种是聚合,另一种是连接。
select least(origin, dest) as od1, greatest(origin, dest) as od2, sum(passengers) as numpassengers
from path t
group by least(origin, dest), greatest(origin, dest)
order by numpassengers
limit 3;
另一种是自连接。如果每个方向只有一行,则可以不聚合:
select p1.origin, p1.dest, p1.passengers + p2.passengers as numpassengers
from path p1 join
path pt2
on p1.origin = p2.dest and p1.dest = p2.origin
where p1.origin < p1.dest
order by numpassengers desc
limit 3;
否则,您需要自连接和聚合,因此第一种方法可能更快:
select p1.origin, p1.dest, sum(p1.passengers + p2.passengers) as numpassengers
from path p1 join
path pt2
on p1.origin = p2.dest and p1.dest = p2.origin
where p1.origin < p1.dest
group by p1.origin, p1.dest
order by numpassengers desc
limit 3;
我不知道哪个效率更高。但是,我怀疑按总和计算的前 3 条路线会在每个方向的前 100 名中。如果是这样,在 numpassengers 上建立一个索引,然后尝试:
select least(origin, dest) as od1, greatest(origin, dest) as od2, sum(passengers) as numpassengers
from path t cross join
(select min(passengers) as cutoff
from (select distinct passengers
from path
order by passengers desc
limit 100
) t
) minp
where numpassengers >= minp.cutoff
group by least(origin, dest), greatest(origin, dest)
order by numpassengers
limit 3;
截断的计算应该只使用索引,大大减少其余查询的负载。
编辑:
如果您没有least()
和greatest()
,只需使用case
语句:
select (case when origin < dest then origin else dest end) as od1,
(case when origin < dest then dest else origin end) as od2,
sum(passengers) as numpassengers
from path t
group by 1, 2
order by numpassengers
limit 3;
您可以在group by
中重复case
语句。但是 Amazon Redshift 允许您在 group by
子句中引用列别名或位置。
关于SQL 自连接和聚合,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21530049/