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

Efficiently running an SQL query over multiple inputs

您好,我有一个模拟快照,目前作为 table 存储在 PostgreSQL 数据库中,快照 table 的模式是

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}

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

由于 table 有大约 1 亿行,所以我更愿意将其作为 SQL 查询来执行,而不是获取所有粒子并在其中使用 numpy.histogram 之类的东西python 在我的本地机器上进行装箱。

方法一

此查询可能有效,例如 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

方法三

如果不切实际,另一种可能值得尝试的完全不同的方法是在 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));

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