何かを返すかどうかを確認する必要があるクエリのカーソルを作成するPL/pgSQL関数を書いています。
私がやっていることはこれです:
クエリが非常に長いクエリ(5つのテーブルと多数の列を結合する)とSELECT ... INTO
クエリは1つのテーブルの列と距離計算用の列を含むため、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;
したがって、私の目的は、座標、半径、試行回数を指定し、その検索ボックス内で店舗を検索して見つけることです。ストアが見つからない場合は、半径を2倍にして、クエリによって何かが返されるか、試行回数に達するまで再試行します。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;
NULL入力を許可しないように関数STRICT
を作成しました。これにより、無限ループが発生する可能性があります。
"Contained"演算子<@
でcirclesを使用する方法に注意してくださいboxesの代わりに 計算はボックスよりも少し高価であると想定しますが、次のようなGistインデックスを使用してクエリをサポートすると、ほとんど問題になりません。
CREATE INDEX store_point_Gist_idx ON store
USING Gist (point(latitude, longitude));
Lat/lonを point
として保存し、式のインデックスを、カラム。どちらの方法でも機能します。クエリがインデックスと一致することを確認して、使用されるようにします。大きなテーブルの大きな違い。
あなたはこれに興味があるかもしれません SOと密接に関連した回答 昨年投稿した-より多くの説明とリンクを付けて。