Postgres 9.1で。特定の郵便番号のタイムゾーンを取得するために1つのクエリを実行しようとしています。データベースにデータをロードし、PostGISを使用して各郵便番号の座標を保存しています。必要なデータを取得するために必要な2つのクエリを次に示します。
SELECT coord FROM public.postal_code WHERE postal_code = 'T1K0T4' LIMIT 1
クエリ時間:6ミリ秒
これにより、postal_code領域の中心の座標がわかります。次に、この座標を使用して、交差するタイムゾーンを見つけます。
SELECT *
FROM public.timezones as tz
WHERE ST_Intersects(ST_GeomFromText('POINT(-112 49)',4326),geom)
クエリ時間:36ミリ秒
2つのクエリを組み合わせると、クエリ時間は7〜8秒にジャンプします。これは私のクエリです:
SELECT *
FROM public.timezones as tz
WHERE ST_Intersects((SELECT coord FROM taduler.postal_code
WHERE postal_code = 'T1K0T4' LIMIT 1),geom)
Postal_codeテーブルのcoord
列とタイムゾーンテーブルのgeom
列に空間インデックスがありますが、サブクエリに使用されていないようです。
誰かがこのクエリを最適化するより良い方法を知っていますか?テーブルの結合など、このクエリのいくつかのバリエーションを試しましたが、すべて同じクエリ速度が得られました。
EXPLAIN ANALYZEからの出力:
Seq Scan on timezones tz (cost=8.37..167.47 rows=136 width=335547) (actual time=4606.136..7274.428 rows=1 loops=1)
Filter: st_intersects($0, (geom)::geography)
InitPlan 1 (returns $0)
-> Limit (cost=0.00..8.37 rows=1 width=128) (actual time=0.011..0.011 rows=1 loops=1)
-> Index Scan using postal_code_idx on postal_code (cost=0.00..8.37 rows=1 width=128) (actual time=0.010..0.010 rows=1 loops=1)
Index Cond: ((postal_code)::text = 'T1K0T4'::text)
Total runtime: 7274.448 ms
次のクエリのEXPLAIN ANALYZE:
SELECT *
FROM public.timezones as tz
JOIN taduler.postal_code as pc on ST_Intersects(pc.coord, tz.geom)
WHERE pc.postal_code = 'T1K0T4'
出力:
Nested Loop (cost=0.00..174.61 rows=1 width=335714) (actual time=4870.908..7572.723 rows=1 loops=1)
Join Filter: ((pc.coord && (tz.geom)::geography) AND (_st_distance(pc.coord, (tz.geom)::geography, 0::double precision, false) < 1e-05::double precision))
-> Index Scan using postal_code_idx on postal_code pc (cost=0.00..8.37 rows=1 width=167) (actual time=0.012..0.019 rows=1 loops=1)
Index Cond: ((postal_code)::text = 'T1K0T4'::text)
-> Seq Scan on timezones tz (cost=0.00..56.08 rows=408 width=335547) (actual time=0.002..2.795 rows=408 loops=1)
Total runtime: 7572.787 ms
クエリプランナーの弱点で実行しているようです:最良のインデックスは、テーブルの結合に使用されない場合があります。ここに同様の問題がありました:
最長の接頭辞を見つけるためのアルゴリズム (「text_pattern_opsによる失敗した試行」の章)
Postgres 9.3では、このバージョンを LEFT JOIN LATERAL
:
SELECT *
FROM (
SELECT coord
FROM taduler.postal_code
WHERE postal_code = 'T1K0T4'
LIMIT 1
) pc
LEFT JOIN LATERAL (
SELECT *
FROM public.timezones tz
WHERE ST_Intersects(pc.coord, tz.geom)
) tz ON TRUE;
同様の何かがうまくいった この関連する答えでの@ypercubeの解決策LATERAL
にはPostgres 9.3以降が必要です。
PostgreSQL 9.1では、最初のクエリをCTEにカプセル化すると役立つかもしれませんが疑わしいです 。 (テストするためのPostGisインストールはここにありません。):
WITH pc AS (
SELECT coord
FROM taduler.postal_code
WHERE postal_code = 'T1K0T4'
LIMIT 1
)
SELECT *
FROM pc
JOIN public.timezones tz ON ST_Intersects(pc.coord, tz.geom);
2つの個別のクエリをカプセル化するplpgsql関数は、確かにうまくいくはずです。
CREATE OR REPLACE FUNCTION f_get_tz(_pc text)
RETURNS SETOF public.timezones AS
$func$
DECLARE
_coord geom;
BEGIN
SELECT coord
INTO _coord
FROM taduler.postal_code
WHERE postal_code = _pc
LIMIT 1;
RETURN QUERY
SELECT *
FROM public.timezones tz
WHERE ST_Intersects(_coord, tz.geom);
END
$func$ LANGUAGE plpgsql;
コール:
SELECT * FROM f_get_tz('T1K0T4');