在多个输入上高效运行SQL查询

时间:2015-01-08 16:05:40

标签: sql performance postgresql query-performance

您好我有一个模拟快照,当前存储在PostgreSQL数据库中作为快照表的架构的表

simdb=> \d isonew_4.snapshot_102
Table "isonew_4.snapshot_102"
Column |  Type   | Modifiers 
--------+---------+-----------
 id     | integer | 
 x      | real    | 
 y      | real    | 
 z      | real    | 
 vx     | real    | 
 vy     | real    | 
 vz     | real    | 
 pot    | real    | 
 mass   | real    | 
Indexes:
    "snapshot_102_id_idx" btree (id) WITH (fillfactor=100)

我有一个查询计算单个半径精细所包含的质量:

SELECT SUM(mass) AS mass
FROM isonew_4.snapshot_102 AS s
WHERE SQRT(s.x^2 + s.y^2 + s.z^2) < {radius}

但是我想在不同的半径上运行它。

由于该表有大约1亿行,所以我更喜欢做SQL查询,而不是抓取所有粒子并在python中使用类似numpy.histogram的东西来进行binning我的机器本地。

1 个答案:

答案 0 :(得分:2)

方法#1

此查询可能有效,例如10,20和25作为半径的连续值:

WITH r(radius) as (values (10),(20),(25))
  SELECT radius, SUM(mass) AS mass
  FROM isonew_4.snapshot_102 AS s CROSS JOIN r
  WHERE SQRT(s.x^2 + s.y^2 + s.z^2) < radius
  GROUP BY radius;

输出有两列:radius和相应的sum(mass)


方法#2

如果由于列表的CROSS JOIN(可能是EXPLAIN或更好EXPLAIN ANALYZE确定),查询速度太慢,那么一种不同的方法肯定会保证对大型扫描的单次扫描table是将所有结果收集到一行中,每个半径一列,生成的查询如下所示:

SELECT 
 sum(case when r < 10 then s.mass else 0 end) as radius10,
 sum(case when r < 20 then s.mass else 0 end) as radius20,
 sum(case when r < 25 then s.mass else 0 end) as radius25
FROM (select mass,SQRT(x^2 + y^2 + z^2) as r from isonew_4.snapshot_102) AS s

方法#3

如果不实用,可能值得尝试的另一种完全不同的方法是在 btree功能索引中预先计算SQRT(x^2 + y^2 + z^2),希望SQL引擎可以使用它与不平等比较。是否发生这种情况以及查询是否更快取决于数据分布。

create index radius_idx on isonew_4.snapshot_102(SQRT(x^2 + y^2 + z^2));

然后使用第一个查询,每次重复单个半径,或者使用GROUP BY的方法#1和所有值一次重复。如果值非常有选择性,则执行速度可能比单个大型顺序扫描更快。