Oxf*_*ist 4 postgresql performance spatial plpgsql query-performance
我正在编写一个 PL/pgSQL 函数,它为我需要检查它是否返回某些内容的查询创建一个游标。
我正在做的是这样的:
我发现,检查,如果该查询返回的东西用光标是最好的选择,因为它(加入5个表和大量列)一个很长的查询和SELECT ... INTO
不正确的顺心,因为我必须创建一个TYPE
自查询具有来自一个表的列和用于距离计算的列。
问题是我目前仅使用游标来检查查询是否在我打开和关闭它的循环中返回了某些内容。一旦查询返回某些内容,我就退出循环并返回查询。我可以马上说,这是我需要的丑陋的解决方法。也许有人可以帮助我解决这个问题。
这是一些显示我目前正在做的事情的代码。
CREATE FUNCTION store_distance(
latitude double precision,
longitude double precision,
radius double precision,
tries integer
)
RETURNS TABLE(
store_id store.id%type,
store_name store.name%type,
distance double precision
)
AS
$$
DECLARE
cur_stores CURSOR FOR
SELECT
store.id,
store.name,
get_distance(latitude, longitude, store.latitude, store.longitude) distance
FROM
store
WHERE
store.latitude BETWEEN (latitude - radius) AND (latitude + radius)
AND store.longitude BETWEEN (longitude - radius) AND (longitude + radius)
ORDER BY
distance ASC;
count int := 0;
storerow RECORD;
BEGIN
LOOP
IF count = tries THEN
EXIT;
END IF;
OPEN cur_stores;
FETCH cur_stores INTO storerow;
IF FOUND THEN
EXIT;
END IF;
radius := radius * 2;
count := count + 1;
CLOSE cur_stores;
END LOOP;
RETURN QUERY
SELECT
store.id,
store.name,
get_distance(latitude, longitude, store.latitude, store.longitude) distance
FROM
store
WHERE
store.latitude BETWEEN (latitude - radius) AND (latitude + radius)
AND store.longitude BETWEEN (longitude - radius) AND (longitude + radius)
ORDER BY
distance ASC;
END;
$$ LANGUAGE PLPGSQL;
Run Code Online (Sandbox Code Playgroud)
所以我的目的是给出坐标、半径和尝试次数,并尝试在该搜索框中查找商店。如果没有找到商店,我将半径加倍并重试,直到查询返回某些内容或达到尝试次数为止。
该RETURN TABLE
部分基本上是因为我想回到的距离,这样RETURN SETOF store
是没有用的。
我认为您根本不需要光标。要缩短代码,您可以只使用视图。为了提高性能,物化视图应该能让你走得更远。Postgres 9.3 具有内置功能,但您可以自己在旧版本中轻松实现它。
考虑这个简化的形式:
CREATE FUNCTION store_distance(_lat double precision
,_long double precision
,_radius double precision
,_tries integer)
RETURNS TABLE(
store_id store.id%type
,store_name store.name%type
,distance double precision) AS
$func$
DECLARE
_ct int := 0;
_pos point := point(_lat, _long);
BEGIN
LOOP
EXIT WHEN _ct >= _tries
OR EXISTS (
SELECT 1 FROM store s
WHERE point(s.latitude, s.longitude) <@ circle(_pos, _radius));
_radius := _radius * 2;
_ct := _ct + 1;
END LOOP;
RETURN QUERY
SELECT s.id, s.name
,get_distance(_lat, _long, s.latitude, s.longitude)
FROM store s
WHERE point(s.latitude, s.longitude) <@ circle(_pos, _radius);
ORDER BY 3;
END
$func$ LANGUAGE plpgsql STRICT;
Run Code Online (Sandbox Code Playgroud)
我使函数STRICT
禁止 NULL 输入,这可能会导致无限循环。
请注意我如何使用带有"Contained" 运算符而不是box 的circles。人们会认为计算比使用 box 稍微贵一点,但是一旦您使用GiST 索引支持您的查询,这几乎不重要,例如:<@
CREATE INDEX store_point_gist_idx ON store
USING gist (point(latitude, longitude));
Run Code Online (Sandbox Code Playgroud)
您可能会考虑将 lat / lon 存储为 point
开始,并将表达式上的索引替换为列上更简单的索引。无论哪种方式都可以,只需确保查询与索引匹配,以便使用它。大桌子的区别很大。
您可能对我去年发布的SO 上的这个密切相关的答案感兴趣- 有更多的解释和链接。
归档时间: |
|
查看次数: |
2998 次 |
最近记录: |