19 votos

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

Me pregunto cuál será la precisión del SELECT DISTINCT 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 eso 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

18voto

NilObject Puntos 7874

Me sorprende que sea tan tosco, pero ahí está. No es DISTINTO, 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 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" implicaría, por desgracia, o bien una enorme pérdida computacional (haciendo una evaluación explícita de ST_Equals() para la llamada al operador) o bien un nuevo código sustancialmente complicado (almacenando valores hash para geometrías más grandes, haciendo pruebas exactas sobre la marcha para las más pequeñas, eligiendo la ruta de código correcta sobre la marcha, etc.)

Y, por supuesto, ahora muchas aplicaciones/usuarios han interiorizado el comportamiento existente, tal y como es, por lo que "mejorarlo" sería una degradación para mucha gente. Usted puede hacer una "exacta" distinta mediante el cálculo de su conjunto en ST_AsBinary(geom) en su lugar, lo que hará la prueba de igualdad exacta en las salidas bytea.

7voto

tucuxi Puntos 5130

Dada la excelente explicación de Paul Ramsey de por qué la siguiente pregunta es qué se puede hacer al respecto. ¿Cómo puede SELECT DISTINCT en campos geométricos y que funcione como se espera?

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

En cambio, esto me pareció más rápido:

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

4voto

Cohen Puntos 1649

Sólo una actualización, para PostGIS 2.4, SELECT DISTINCT funciona correctamente para los datos de puntos del 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)

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)

i-Ciencias.com

I-Ciencias es una comunidad de estudiantes y amantes de la ciencia en la que puedes resolver tus problemas y dudas.
Puedes consultar las preguntas de otros usuarios, hacer tus propias preguntas o resolver las de los demás.

Powered by:

X