¿Cuál es la precisión de SELECT DISTINCT en la columna de geometría PostGIS?

19

Me pregunto cuál es la precisión del SELECT DISTINCToperador en una geometría PostGIS. En mi sistema, la siguiente consulta me da un recuento de 5, lo que significa que los puntos insertados se consideran iguales si difieren en menos de 1e-5 y no estoy seguro de si esa es una característica de PostGIS, un problema de mi instalación o un error

¿Alguien sabe si ese es el comportamiento esperado?

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)

Estoy usando:

$ psql --version
psql (PostgreSQL) 9.3.1

y

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

en OSX 10.9

tapa amarilla
fuente

Respuestas:

18

Me sorprende que sea tan grosero, pero ahí está. No es DISTINCT, per se, es el operador '=', que se define para la geometría como 'igualdad de las claves de índice', lo que significa prácticamente 'igualdad de los cuadros delimitadores de 32 bits'.

Puedes ver el mismo efecto simplemente usando '=' directamente,

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;

Hacer que '=' se comporte "intuitivamente" desafortunadamente implicaría una gran pérdida computacional (hacer una evaluación ST_Equals () explícita para la llamada del operador) o algún código nuevo y sustancial (almacenar valores hash para geometrías más grandes, hacer pruebas exactas sobre la marcha para las más pequeñas unos, elegir la ruta de código correcta sobre la marcha, etc.)

Y, por supuesto, ahora muchas aplicaciones / usuarios han internalizado el comportamiento existente, tal como es, por lo que "mejorar" sería una degradación para muchas personas. En su lugar, puede hacer una distinción "exacta" calculando su conjunto en ST_AsBinary (geom), que realizará pruebas de igualdad exactas en las salidas de bytea.

Paul Ramsey
fuente
¿Y podemos suponer que ST_AsBinary (geom) es una operación relativamente muy rápida?
Martin F
Gracias por su respuesta, esto explica bien el comportamiento. De hecho, estoy trabajando en un proyecto geodjango, así que usaré el __equalsfiltro allí, que se traduce en la función ST_Equals, creo.
yellowcap
1
Sí, ST_AsBinary es rápido. Las pruebas de igualdad en bytea probablemente involucren memcmp, que es una operación muy rápida, por lo que no debería ser demasiado terrible.
Paul Ramsey
¿Qué propones aquí, @PaulRamsey? SELECT DISTINCT ST_AsBinary(geom)? Eso da una representación binaria de geomcomo resultado. Podría hacerlo SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);(creo que MAX()se requiere una función agregada como SELECTporque la GROUP BYcláusula está utilizando la ST_AsBinary()función return, no el campo en sí). ¿Se ve bien?
Martin Burch
7

Dada la excelente explicación de Paul Ramsey de por qué la siguiente pregunta es qué se puede hacer al respecto. ¿Cómo funciona SELECT DISTINCTen los campos de geometría y hace que funcione como se espera?

En la respuesta de Paul, propuse usar SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);pero MAX()es lento, aparentemente requiere un escaneo de tabla.

En cambio, encontré que esto es más rápido:

SELECT DISTINCT ON (ST_AsBinary(geom)) geom FROM the_table;
Martin Burch
fuente
4

Solo una actualización, para PostGIS 2.4, SELECT DISTINCTfunciona correctamente para los datos de puntos en el 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)

Y

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)
tinlyx
fuente