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

18

Me pregunto cuál es la precisión del operador SELECT DISTINCT en una geometría PostGIS. En mi sistema, la siguiente consulta me da un conteo 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

    
pregunta yellowcap 20.12.2013 - 17:54

3 respuestas

17

Me sorprende que sea tan tosco, 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', 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 involucraría una gran pérdida computacional (haciendo una evaluación explícita de ST_Equals () para la llamada del operador) o algún código nuevo sustancialmente nuevo (almacenando valores hash para geometrías más grandes, haciendo pruebas exactas en el vuela para los más pequeños, eligiendo la ruta de código correcta sobre la marcha, etc.)

Y, por supuesto, ahora muchas aplicaciones / usuarios han internalizado el comportamiento existente, tal como está, por lo que "mejorar" sería una rebaja para muchas personas. Puede hacer una distinción "exacta" calculando su conjunto en ST_AsBinary (geom), lo que hará una prueba de igualdad exacta en las salidas de bytea.

    
respondido por el Paul Ramsey 20.12.2013 - 18:43
6

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

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

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

SELECT DISTINCT ON (ST_AsBinary(geom)) geom FROM the_table;
    
respondido por el Martin Burch 17.12.2014 - 00:09
3

Solo una actualización, para PostGIS 2.4, SELECT DISTINCT funciona 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)
    
respondido por el tinlyx 26.02.2018 - 03:47

Lea otras preguntas en las etiquetas