在多个输入上高效地运行 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
和所有值。如果值非常有选择性,执行速度可能比单个大型顺序扫描快得多。
您好,我有一个模拟快照,目前作为 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
和所有值。如果值非常有选择性,执行速度可能比单个大型顺序扫描快得多。