Qual è la precisione di SELECT DISTINCT sulla colonna della geometria PostGIS?


19

Mi chiedo quale sia la precisione SELECT DISTINCTdell'operatore su una geometria PostGIS. Sul mio sistema, la seguente query mi dà un conteggio di 5, il che significa che i punti inseriti sono considerati uguali se differiscono di meno di 1e-5 e non sono sicuro che sia una caratteristica di PostGIS, un problema della mia installazione o un bug.

Qualcuno sa se questo è il comportamento previsto?

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)

Sto usando:

$ psql --version
psql (PostgreSQL) 9.3.1

e

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

su OSX 10.9

Risposte:


18

Sono sorpreso che sia abbastanza grossolano, ma è così. Non è DISTINCT, di per sé, è l'operatore '=', che è definito per la geometria come 'uguaglianza delle chiavi di indice' che significa praticamente 'uguaglianza delle scatole di delimitazione a 32 bit'.

Puoi vedere lo stesso effetto semplicemente usando '=' direttamente,

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;

Fare '=' comportarsi "in modo intuitivo" sfortunatamente comporterebbe o un'enorme perdita computazionale (facendo esplicita valutazione ST_Equals () per la chiamata dell'operatore) o qualche nuovo sostanziale codice complicato (memorizzazione di valori hash per geometrie più grandi, facendo test precisi al volo per piccoli quelli, scegliendo al volo il giusto percorso del codice, ecc.)

E ovviamente ora molte applicazioni / utenti hanno interiorizzato il comportamento esistente, così com'è, quindi "migliorare" sarebbe un downgrade per molte persone. Puoi fare un "esatto" distinto calcolando invece il tuo set su ST_AsBinary (geom), che eseguirà test di uguaglianza esatta sugli output bytea.


E possiamo supporre che ST_AsBinary (geom) sia un'operazione relativamente molto veloce?
Martin F,

Grazie per la tua risposta, questo spiega bene il comportamento. In realtà sto lavorando a un progetto geodjango, quindi userò __equalslì il filtro, che si traduce nella funzione ST_Equals, credo.
giallo

1
Sì, ST_AsBinary è veloce. I test di uguaglianza su bytea probabilmente coinvolgono memcmp, che è un'operazione molto veloce, quindi non dovrebbe essere troppo terribile.
Paul Ramsey,

Cosa proponi qui, @PaulRamsey? SELECT DISTINCT ST_AsBinary(geom)? Ciò fornisce una rappresentazione binaria di geomcome risultato. Si potrebbe fare SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);(penso che una funzione aggregata come MAX()è richiesta nel SELECTperché la GROUP BYclausola sta usando la ST_AsBinary()funzione return, non il campo stesso.) Ti sembra buono?
Martin Burch,

7

Data l'eccellente spiegazione di Paul Ramsey sul perché la prossima domanda è cosa si può fare al riguardo. Come si fa SELECT DISTINCTsui campi della geometria e si ottiene come previsto?

Nella risposta di Paul, ho proposto di usare SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);ma MAX()è lento, apparentemente richiede una scansione della tabella.

Invece, ho trovato questo per essere più veloce:

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

4

Solo un aggiornamento, per PostGIS 2.4, SELECT DISTINCTfunziona correttamente per i dati dei punti nel PO:

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)

E

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)
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.