PostGIS geometri sütununda SELECT DISTINCT'in hassasiyeti nedir?


19

SELECT DISTINCTPostGIS geometrisinde operatörün hassasiyetinin ne olduğunu merak ediyorum . Sistemimde, aşağıdaki sorgu bana 5 sayısı veriyor, yani eklenen noktalar 1e-5'ten daha az farklıysa eşit kabul ediliyor ve bunun kurulumumun bir sorunu olan PostGIS'in bir özelliği olup olmadığından emin değilim veya bir hata.

Herkes bunun beklenen davranış olup olmadığını biliyor mu?

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)

Ben kullanıyorum:

$ psql --version
psql (PostgreSQL) 9.3.1

ve

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'da

Yanıtlar:


18

Çok kaba olmasına şaşırdım, ama işte burada. DISTINCT değil, aslında, geometri için 'indeks anahtarlarının eşitliği' olarak tanımlanan '=' operatörü, pratikte '32 bit sınırlama kutularının eşitliği' anlamına gelir.

Aynı efekti doğrudan '=' kullanarak doğrudan görebilirsiniz,

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;

'=' "Sezgisel" davranmak maalesef büyük bir hesaplama kaybı (operatör çağrısı için açık ST_Equals () değerlendirmesi yapmak) veya bazı önemli yeni karmaşık kod (daha büyük geometriler için hash değerlerini saklamak, daha küçükler için anında kesin testler yapmak) olanlar, doğru kod yolunu seçerken, vb.)

Ve elbette şimdi birçok uygulama / kullanıcı mevcut davranışı içselleştirdi, bu yüzden "iyileştirme" birçok kişi için bir düşüş olacaktır. ST_AsBinary (geom) üzerindeki setinizi hesaplayarak, "bytea" çıkışları üzerinde kesin eşitlik testi yapacak "kesin" bir ayrım yapabilirsiniz.


Ve ST_AsBinary'in (geom) nispeten çok hızlı bir işlem olduğunu varsayabilir miyiz?
Martin F

Cevabınız için teşekkürler, bu davranışı iyi açıklıyor. Aslında bir geodjango projesi üzerinde çalışıyorum, bu yüzden sanırım __equalsST_Equals işlevine çeviren filtre kullanacağım .
yellowcap

1
Evet ST_AsBinary hızlı. Baytea üzerinde eşitlik testleri muhtemelen çok hızlı bir işlem olan memcmp'yi içerir, bu yüzden çok korkunç olmamalıdır.
Paul Ramsey

Burada ne öneriyorsun @PaulRamsey? SELECT DISTINCT ST_AsBinary(geom)? Bu geomsonuç olarak ikili bir gösterim verir . Sen yapabilirdi SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);(gibi bir toplama işlevi düşünmek MAX()gereklidir SELECTçünkü GROUP BYhüküm kullanıyor ST_AsBinary()fonksiyon dönüşü değil, tarla kendisi.) O bakış güzel mi?
Martin Burch

7

Paul Ramsey mükemmel açıklama göz önüne alındığında neden sonraki soru nedir bu konuda yapılabilir. SELECT DISTINCTGeometri alanlarında nasıl çalışıyorsunuz ve beklendiği gibi çalışmasını sağlıyor musunuz?

Paul'un cevabında, kullanmayı önerdim SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);ama MAX()yavaş, bir tablo taraması gerektiriyor.

Bunun yerine, bunu daha hızlı buldum:

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

4

Sadece PostGIS 2.4 için bir güncelleme, OP'deki SELECT DISTINCTpuan verileri için doğru şekilde çalışır:

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)

Ve

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)
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.