PostGISジオメトリ列のSELECT DISTINCTの精度はどのくらいですか?


19

SELECT DISTINCTPostGISジオメトリでの演算子の精度はどのくらいかと思います。私のシステムでは、次のクエリは5をカウントします。つまり、挿入されたポイントが1e-5未満の差であれば等しいと見なされ、インストールの問題であるPostGISの機能であるかどうかはわかりませんまたはバグ。

それが予想される動作であるかどうかは誰にもわかりますか?

CREATE TEMP TABLE test (geom geometry);
INSERT INTO test
    VALUES 
        (St_GeomFromText('POINT (0.1 0.1)')),
        (St_GeomFromText('POINT (0.001 0.001)')),
        (St_GeomFromText('POINT (0.0001 0.0001)')),
        (St_GeomFromText('POINT (0.00001 0.00001)')),
        (St_GeomFromText('POINT (0.000001 0.000001)')),
        (St_GeomFromText('POINT (0.0000001 0.0000001)')),
        (St_GeomFromText('POINT (0.00000001 0.00000001)')),
        (St_GeomFromText('POINT (0.000000001 0.000000001)'));

SELECT COUNT(*) FROM (SELECT DISTINCT geom FROM test) AS test;

 count 
-------
     5
(1 row)

使っています:

$ psql --version
psql (PostgreSQL) 9.3.1

そして

SELECT PostGIS_full_version();
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
POSTGIS="2.1.1 r12113" GEOS="3.4.2-CAPI-1.8.2 r3921" PROJ="Rel. 4.8.0, 6 March 2012" GDAL="GDAL 1.10.1, released 2013/08/26" LIBXML="2.7.3" LIBJSON="UNKNOWN" RASTER

OSX 10.9で

回答:


18

私はそれがとても粗いことに驚いていますが、そこにはあります。それ自体はDISTINCTではなく、「=」演算子です。これは、ジオメトリに対して「インデックスキーの等価性」として定義され、実質的に「32ビット境界ボックスの等価性」を意味します。

「=」を直接使用するだけで同じ効果を確認できますが、

select 'POINT (0.000000001 0.000000001)'::geometry = 'POINT (0.000000001 0.000001)'::geometry;

select 'POINT (0.000000001 0.000000001)'::geometry = 'POINT (0.000000001 0.00001)'::geometry;

「=」を「直感的に」動作させるには、残念ながら、膨大な計算の損失(演算子呼び出しの明示的なST_Equals()評価を行う)またはいくつかの実質的な新しい複雑なコード(より大きなジオメトリのハッシュ値を格納し、その場で正確なテストを実行する)実行中に適切なコードパスを選択するなど)

そしてもちろん、今では多くのアプリケーション/ユーザーが既存の振る舞いを内部化しているため、「改善する」ことは多くの人々にとって格下げになります。代わりに、ST_AsBinary(geom)でセットを計算することにより、「正確な」別個の処理を実行できます。これにより、bytea出力で正確な等価性テストが実行されます。


また、ST_AsBinary(geom)が比較的非常に高速な操作であると想定できますか?
マーティンF

あなたの答えをありがとう、これは行動をよく説明します。実際にジオジャンゴプロジェクトに取り組んでいるので、そこで__equalsフィルターを使用します。これは、ST_Equals関数に変換されると思います。
yellowcap

1
はいST_AsBinaryは高速です。byteaの平等テストにはおそらくmemcmpが関係しますが、これは非常に高速な操作なので、あまりひどいものではありません。
ポールラムジー

@PaulRamsey、ここで何を提案していますか?SELECT DISTINCT ST_AsBinary(geom)geom結果としてのバイナリ表現が得られます。あなたはできますSELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);(句はフィールド自体ではなく関数の戻り値を使用MAX()しているSELECTため、のような集約関数が必要だと思います)。GROUP BYST_AsBinary()
マーティンバーチ14

7

次の質問がそれについてができるのというポール・ラムジーの優れた説明を考えると。ジオメトリフィールドではどのように機能し、期待どおりに機能しますか?SELECT DISTINCT

Paulの回答では、使用することを提案しましSELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);MAX()が、速度が遅く、明らかにテーブルスキャンが必要です。

代わりに、私はこれが速いことがわかりました:

SELECT DISTINCT ON (ST_AsBinary(geom)) geom FROM the_table;

4

PostGIS 2.4の更新SELECT DISTINCTは、OP内のポイントデータに対して正しく動作します。

CREATE TEMP TABLE test (geom geometry);
CREATE TABLE
user=> INSERT INTO test
user->     VALUES 
user->         (St_GeomFromText('POINT (0.1 0.1)')),
user->         (St_GeomFromText('POINT (0.001 0.001)')),
user->         (St_GeomFromText('POINT (0.0001 0.0001)')),
user->         (St_GeomFromText('POINT (0.00001 0.00001)')),
user->         (St_GeomFromText('POINT (0.000001 0.000001)')),
user->         (St_GeomFromText('POINT (0.0000001 0.0000001)')),
user->         (St_GeomFromText('POINT (0.00000001 0.00000001)')),
user->         (St_GeomFromText('POINT (0.000000001 0.000000001)'));
INSERT 0 8
user=> 
user=> SELECT COUNT(*) FROM (SELECT DISTINCT geom FROM test) AS test;
 count 
-------
     8
(1 row)

そして

user=> select 'POINT (0.000000001 0.000000001)'::geometry = 'POINT (0.000000001 0.000001)'::geometry;
 ?column? 
----------
 f
(1 row)

user=> 
user=> select 'POINT (0.000000001 0.000000001)'::geometry = 'POINT (0.000000001 0.00001)'::geometry;
 ?column? 
----------
 f
(1 row)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.