在 PostgreSQL 中执行多个平均查询
Performing Multiple Avg Queries in PostgreSQL
我在执行以下操作时遇到了一些问题:我有一个名为 entries 的数据库 table,它(出于所有意图和目的)除了主键之外还有 3 列:value
, gps_lat
、gps_long
都是双打。
我的最终目标是能够定义一个网格,比如 100x100,具有一定的间隔,并以给定的纬度和经度值为界,对于网格的每个正方形,我想计算其中所有点的平均值那个方格。但是,我在有效地执行此操作时遇到了很多麻烦。
部分问题是我想将其设置为存储过程或查询,我可以用一段代码生成并稍后重用,因为每次我 运行 查询网格将不一样(因此缓存几乎不成问题)。
我第一次尝试这样做是定义以下函数:
CREATE OR REPLACE FUNCTION gridSquareAverageValue (double precision
, double precision, double precision, double precision)
RETURNS double precision as $avgValue$
declare
avgValue double precision;
BEGIN
SELECT AVG(value) into avgValue FROM entries
WHERE gps_lat BETWEEN AND AND gps_long BETWEEN AND ;
RETURN avgValue;
END;
$avgValue$ LANGUAGE plpgsql;
这个函数工作得很好,完全可以满足我的需要,除了它只针对一个网格方块。 运行 100x100 网格的函数涉及 10,000 个单独的查询,因此非常慢。
下一次尝试是这样的:
WITH Grid(lat_offset,long_offset) AS
(SELECT *
FROM generate_series(1,10) lat_offset
CROSS JOIN generate_series(1,10) long_offset)
SELECT AVG(value)
FROM Grid
JOIN entries
ON entries.gps_lat BETWEEN 41.79604807005128 + (0.000247908106797 * Grid.lat_offset)
AND 41.82083888073101 + (0.002479081067973 * (Grid.lat_offset + 1))
AND entries.gps_long BETWEEN -72.2759199142456 + (0.000527858734131 * Grid.long_offset)
AND -72.22313404083252 + (0.005278587341308 * (Grid.long_offset + 1))
GROUP BY lat_offset,long_offset;
这不知何故变得更糟。我试图生成一系列偏移量,然后将其与条目的 table 连接起来,强制每个条目进入一个框,该框使用您在上面看到的数学计算得出。这太慢了。我试图让它只输出值而不计算平均值,它花费的时间比 运行 宁 10k 个人查询还要长。
以上也可能是最有前途的方法,因为在生成两个系列的笛卡尔连接后我真正想做的就是在一个简单的函数中使用它们,但我想不出任何合适的方法来做到这一点,除了你在上面看到的=/
最后我尝试了这个:
# height width lat start lat interval long start long interval
CREATE OR REPLACE FUNCTION gridAverageValue (integer, integer, double precision, double precision, double precision, double precision)
RETURNS TABLE (avg double precision) as $restbl$
BEGIN
SELECT * INTO $restbl$ FROM entries WHERE 1 = 2;
FOR lat_offset IN 0.. LOOP
FOR long_offset IN 0.. LOOP
INSERT INTO restbl
SELECT AVG(value)
FROM entries
WHERE gps_lat
BETWEEN + ( * lat_offset) AND + ( * (lat_offset + 1))
AND gps_long
BETWEEN + ( * long_offset) AND + ( * (long_offset + 1));
END LOOP;
END LOOP;
RETURN QUERY SELECT * FROM restbl;
END;
$restbl$ LANGUAGE plpgsql;
最后一次尝试出现了一堆语法错误,老实说我不知道它是从哪里来的。总体思路是生成一堆查询,最终计算出我关心的值。
如果有人对如何解决上述任何方法提出建议,我们将不胜感激。
仅填充单元格
使用内置函数width_bucket()
仅获取在entries
中具有一个或多个匹配行的网格单元格:
对于 box(point(_lat_start, _long_start), point(_lat_end, _long_end))
外框内 100 x 100 个单元格的网格:
SELECT width_bucket(gps_lat , _lat_start , _lat_end , 100) AS grid_lat
, width_bucket(gps_long, _long_start, _long_end, 100) AS grid_long
, avg(value) AS avg_val
FROM entries
WHERE point(gps_lat, gps_long) <@ box(point(_lat_start, _long_start)
, point(_lat_end , _long_end))
GROUP BY 1,2
ORDER BY 1,2;
<@
is the "contained in" operator for geometric types.
很容易将其包装成一个函数并参数化外框和网格单元的数量。
多列 GiST 表达式 index 将有助于提高性能 if 只有一小部分行位于外框内。您需要先安装 btree_gist 模块,每个数据库安装一次:
然后:
CREATE INDEX entries_point_idx ON entries
USING gist (point(gps_lat, gps_long), value);
仅当您可以在 Postgres 9.2+ 中从中获得仅索引扫描时,将 value
添加到索引才有意义。
如果您正在阅读 table 的大部分内容,则不需要索引,并且 运行 简单 a between x and y
检查 [=19] 可能更便宜=] 子句。
这是假设一个平坦的地球(这可能足以满足您的目的)。如果要精确,则必须更深入地研究 PostGIS。
网格中的所有单元格
要获得 所有 个单元格,请使用 LEFT JOIN
到预生成的网格,就像您已经尝试过的那样:
SELECT grid_lat, grid_long, g.avg_val -- or use COALESCE
FROM generate_series(1,100) grid_lat
CROSS JOIN generate_series(1,100) grid_long
LEFT JOIN (<query from above>) g USING (grid_lat, grid_long)
相关:
- Aggregating (x,y) coordinate point clouds in PostgreSQL
我在执行以下操作时遇到了一些问题:我有一个名为 entries 的数据库 table,它(出于所有意图和目的)除了主键之外还有 3 列:value
, gps_lat
、gps_long
都是双打。
我的最终目标是能够定义一个网格,比如 100x100,具有一定的间隔,并以给定的纬度和经度值为界,对于网格的每个正方形,我想计算其中所有点的平均值那个方格。但是,我在有效地执行此操作时遇到了很多麻烦。
部分问题是我想将其设置为存储过程或查询,我可以用一段代码生成并稍后重用,因为每次我 运行 查询网格将不一样(因此缓存几乎不成问题)。
我第一次尝试这样做是定义以下函数:
CREATE OR REPLACE FUNCTION gridSquareAverageValue (double precision
, double precision, double precision, double precision)
RETURNS double precision as $avgValue$
declare
avgValue double precision;
BEGIN
SELECT AVG(value) into avgValue FROM entries
WHERE gps_lat BETWEEN AND AND gps_long BETWEEN AND ;
RETURN avgValue;
END;
$avgValue$ LANGUAGE plpgsql;
这个函数工作得很好,完全可以满足我的需要,除了它只针对一个网格方块。 运行 100x100 网格的函数涉及 10,000 个单独的查询,因此非常慢。
下一次尝试是这样的:
WITH Grid(lat_offset,long_offset) AS
(SELECT *
FROM generate_series(1,10) lat_offset
CROSS JOIN generate_series(1,10) long_offset)
SELECT AVG(value)
FROM Grid
JOIN entries
ON entries.gps_lat BETWEEN 41.79604807005128 + (0.000247908106797 * Grid.lat_offset)
AND 41.82083888073101 + (0.002479081067973 * (Grid.lat_offset + 1))
AND entries.gps_long BETWEEN -72.2759199142456 + (0.000527858734131 * Grid.long_offset)
AND -72.22313404083252 + (0.005278587341308 * (Grid.long_offset + 1))
GROUP BY lat_offset,long_offset;
这不知何故变得更糟。我试图生成一系列偏移量,然后将其与条目的 table 连接起来,强制每个条目进入一个框,该框使用您在上面看到的数学计算得出。这太慢了。我试图让它只输出值而不计算平均值,它花费的时间比 运行 宁 10k 个人查询还要长。
以上也可能是最有前途的方法,因为在生成两个系列的笛卡尔连接后我真正想做的就是在一个简单的函数中使用它们,但我想不出任何合适的方法来做到这一点,除了你在上面看到的=/
最后我尝试了这个:
# height width lat start lat interval long start long interval
CREATE OR REPLACE FUNCTION gridAverageValue (integer, integer, double precision, double precision, double precision, double precision)
RETURNS TABLE (avg double precision) as $restbl$
BEGIN
SELECT * INTO $restbl$ FROM entries WHERE 1 = 2;
FOR lat_offset IN 0.. LOOP
FOR long_offset IN 0.. LOOP
INSERT INTO restbl
SELECT AVG(value)
FROM entries
WHERE gps_lat
BETWEEN + ( * lat_offset) AND + ( * (lat_offset + 1))
AND gps_long
BETWEEN + ( * long_offset) AND + ( * (long_offset + 1));
END LOOP;
END LOOP;
RETURN QUERY SELECT * FROM restbl;
END;
$restbl$ LANGUAGE plpgsql;
最后一次尝试出现了一堆语法错误,老实说我不知道它是从哪里来的。总体思路是生成一堆查询,最终计算出我关心的值。
如果有人对如何解决上述任何方法提出建议,我们将不胜感激。
仅填充单元格
使用内置函数width_bucket()
仅获取在entries
中具有一个或多个匹配行的网格单元格:
对于 box(point(_lat_start, _long_start), point(_lat_end, _long_end))
外框内 100 x 100 个单元格的网格:
SELECT width_bucket(gps_lat , _lat_start , _lat_end , 100) AS grid_lat
, width_bucket(gps_long, _long_start, _long_end, 100) AS grid_long
, avg(value) AS avg_val
FROM entries
WHERE point(gps_lat, gps_long) <@ box(point(_lat_start, _long_start)
, point(_lat_end , _long_end))
GROUP BY 1,2
ORDER BY 1,2;
<@
is the "contained in" operator for geometric types.
很容易将其包装成一个函数并参数化外框和网格单元的数量。
多列 GiST 表达式 index 将有助于提高性能 if 只有一小部分行位于外框内。您需要先安装 btree_gist 模块,每个数据库安装一次:
然后:
CREATE INDEX entries_point_idx ON entries
USING gist (point(gps_lat, gps_long), value);
仅当您可以在 Postgres 9.2+ 中从中获得仅索引扫描时,将 value
添加到索引才有意义。
如果您正在阅读 table 的大部分内容,则不需要索引,并且 运行 简单 a between x and y
检查 [=19] 可能更便宜=] 子句。
这是假设一个平坦的地球(这可能足以满足您的目的)。如果要精确,则必须更深入地研究 PostGIS。
网格中的所有单元格
要获得 所有 个单元格,请使用 LEFT JOIN
到预生成的网格,就像您已经尝试过的那样:
SELECT grid_lat, grid_long, g.avg_val -- or use COALESCE
FROM generate_series(1,100) grid_lat
CROSS JOIN generate_series(1,100) grid_long
LEFT JOIN (<query from above>) g USING (grid_lat, grid_long)
相关:
- Aggregating (x,y) coordinate point clouds in PostgreSQL