PostgreSQLで範囲検索

ホットペッパーAPIでお店の情報をDBに投入。

投入先のテーブル構成


CREATE TABLE shop
(
"ID" integer NOT NULL,
"NAME" character varying(120) NOT NULL,
"KANA" character varying(255),
"PREF_CODE" smallint NOT NULL,
"ADDRESS" character varying(408) NOT NULL,
"IDO_JGD" double precision NOT NULL,
"KEIDO_JGD" double precision NOT NULL,
"OPENING_HOURS" character varying(255),
"REGULAR_HOLIDAY" character varying(255),
"URL_PC" character varying(255),
"URL_MB" character varying(255),
"IDO_AREA" integer NOT NULL,
"KEIDO_AREA" integer NOT NULL,
"GEO_AREA" point NOT NULL,
CONSTRAINT shop_pkey PRIMARY KEY ("ID")
);

CREATE INDEX shop_idx3
ON shop
USING btree
("IDO_AREA", "KEIDO_AREA");

CREATE INDEX shop_idx4
ON shop
USING gist
(circle("GEO_AREA", 0::double precision));


INSERT時のポイント

緯度経度を以下のように1000倍した値をテーブルに投入する。

"IDO_AREA" ← Integer.toString((int) (lat * 1000.0))した値
"KEIDO_AREA"← Integer.toString((int) (lon * 1000.0))した値
"GEO_AREA"← POINT(Integer.toString((int) (lat * 1000.0)), Integer.toString((int) (lat * 1000.0)))


具体的なINSERT文



INSERT INTO SHOP (
"ID",
"NAME",
"PREF_CODE",
"ADDRESS",
"IDO_JGD",
"KEIDO_JGD",
"IDO_AREA",
"KEIDO_AREA",
"GEO_AREA")
VALUES (
1,
"ほげほげ店"
13,
"東京都新宿区",
35.660671535,
139.6960513532,
35660,
139696,
POINT(35660, 139696)
);


検索してみる


select * from shop where circle("GEO_AREA" ,0) @ circle(point(35660,139696), 10) order by "GEO_AREA" <-> point(35660,139696) limit 500;


おぉ!見事検索できました。
インデックスが使われているかをみてみる。

explain analyze select * from shop where circle("GEO_AREA" ,0) @ circle(point(35660,139696), 10) order by "GEO_AREA" <-> point(35660,139696) limit 500;


おぉ!見事インデックスも使われてますね!

ただ、問題は1000倍した緯度経度の基点として検索すること。
上記のSQLはある点から近い順に500件取得する。
としてますが、そのある点は実際の緯度経度から若干ずれてます。
10~50m位と思われますが当然誤差が発生する。
1000倍を10000倍と値を増やしていけばいいけどとりあえずは気にしないって方向で。

参考にさせて頂いたURL
http://neta.ywcafe.net/000597.html
ありがとうございました。
めちゃめちゃ助かりました。

0 コメント: