PostgresとPostGISを使ってクエリを書きたいです。rgeo
Railsも、、、rgeo-activerecord
で使用していますactiverecord-postgis-adapter
が、Railsのものはそれほど重要ではありません。
テーブル構造:
measurement
- int id
- int anchor_id
- Point groundtruth
- data (not important for the query)
データ例:
id | anchor_id | groundtruth | data
-----------------------------------
1 | 1 | POINT(1 4) | ...
2 | 3 | POINT(1 4) | ...
3 | 2 | POINT(1 4) | ...
4 | 3 | POINT(1 4) | ...
-----------------------------------
5 | 2 | POINT(3 2) | ...
6 | 4 | POINT(3 2) | ...
-----------------------------------
7 | 1 | POINT(4 3) | ...
8 | 1 | POINT(4 3) | ...
9 | 1 | POINT(4 3) | ...
10 | 5 | POINT(4 3) | ...
11 | 3 | POINT(4 3) | ...
このテーブルは、view
(数百万行の)ルックアップを高速化するために手動で作成されたものです。そうしないと、8つのテーブルを結合する必要があり、さらに遅くなります。しかし、それは問題の一部ではありません。
シンプルバージョン:
パラメーター:
- 点
p
- int
d
クエリが行うべきこと:
1.クエリはfromポイントgroundtruth
を持つすべてのポイントを探しますdistance < d
p
そのためのSQLは非常に簡単です。WHERE st_distance(groundtruth, p) < d
2.groundtruth
これで、ポイントとそのポイントのリストができましたanchor_id
。上の表からわかるように、複数の同一のgroundtruth-anchor_idタプルを持つことができます。例:anchor_id=3
およびgroundtruth=POINT(1 4)
。
3.次に、同じタプルの1つをランダムに選択して、同じタプルを削除します(!)。単純に最初のものを取りませんか?data
列が違うからです。
SQLでランダムな行を選択する:SELECT ... ORDER BY RANDOM() LIMIT 1
これらすべてに関する私の問題は次のとおりです。SQLLOOP
と多数のサブクエリを使用した解決策を想像できますがGROUP BY
、それを高速化する他の方法を使用した解決策は確かにあります。
完全版:
基本的に上記と同じですが、1つの違いがあります。入力パラメータが変更されます。
- たくさんのポイント
p1
..。p312456345
- まだ1つ
d
LOOP
単純なクエリが機能している場合、これはSQLでを使用して実行できます。しかし、データベースは本当に巨大なので、おそらくより良い(そしてより速い)解決策があるでしょう!
解決
WITH ps AS (SELECT unnest(p_array) AS p)
SELECT DISTINCT ON (anchor_id, groundtruth)
*
FROM measurement m, ps
WHERE EXISTS (
SELECT 1
FROM ps
WHERE st_distance(m.groundtruth, ps.p) < d
)
ORDER BY anchor_id, groundtruth, random();
Erwin Brandstetterに感謝します!