Какова точность SELECT DISTINCT в столбце геометрии PostGIS?

19

Интересно, какова точность SELECT DISTINCTоператора в геометрии PostGIS. В моей системе следующий запрос дает мне счет 5, что означает, что вставленные точки считаются равными, если они отличаются менее чем на 1e-5, и я не уверен, является ли это функцией PostGIS, проблема моей установки или ошибка.

Кто-нибудь знает, является ли это ожидаемым поведением?

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)

Я использую:

$ psql --version
psql (PostgreSQL) 9.3.1

и

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

yellowcap
источник

Ответы:

18

Я удивлен, что это так грубо, но это так. Это не DISTINCT, по сути, это оператор '=', который для геометрии определяется как 'равенство ключей индекса', что означает практически 'равенство 32-битных ограничивающих прямоугольников'.

Вы можете увидеть тот же эффект, просто используя '=' напрямую,

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;

Выполнение «=» поведения «интуитивно», к сожалению, повлечет за собой либо огромные вычислительные потери (выполнение явной оценки ST_Equals () для вызова оператора), либо некоторый существенный новый сложный код (сохранение значений хеш-функции для больших геометрий, выполнение точных тестов на лету для меньших выбор правильного пути к коду на лету и т. д.)

И, конечно, сейчас многие приложения / пользователи усвоили существующее поведение, такое, как оно есть, поэтому «улучшение» было бы понижение для многих людей. Вместо этого вы можете сделать «точное» различение, рассчитав свой набор на ST_AsBinary (geom), что позволит выполнить точное тестирование на равенство для выходных данных bytea.

Пол Рэмси
источник
И можем ли мы предположить, что ST_AsBinary (geom) является относительно очень быстрой операцией?
Мартин Ф
Спасибо за ваш ответ, это хорошо объясняет поведение. На самом деле я работаю над проектом geodjango, поэтому я буду использовать там __equalsфильтр, который, как мне кажется, переводится в функцию ST_Equals.
yellowcap
1
Да, ST_AsBinary - это быстро. Тесты на равенство в bytea, вероятно, включают memcmp, который является очень быстрым операционным процессом, поэтому не должен быть слишком ужасным.
Пол Рэмси
Что вы предлагаете здесь, @PaulRamsey? SELECT DISTINCT ST_AsBinary(geom)? Это дает двоичное представление geomкак результат. Вы могли бы сделать SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);(я думаю, что агрегатная функция, как MAX()требуется, в выражении SELECTпотому, что в GROUP BYпредложении используется ST_AsBinary()функция return, а не само поле.) Это выглядит хорошо?
Мартин Берч
7

Учитывая превосходное объяснение Пола Рэмси, почему следующий вопрос - что с этим можно сделать. Как вы работаете SELECT DISTINCTс геометрическими полями и работаете ли вы так, как ожидаете?

В ответе Пола я предложил использовать, SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);но MAX()медленно, по-видимому, требуя сканирования таблицы.

Вместо этого я обнаружил, что это быстрее:

SELECT DISTINCT ON (ST_AsBinary(geom)) geom FROM the_table;
Мартин Берч
источник
4

Просто обновление для PostGIS 2.4 SELECT DISTINCTработает правильно для данных точек в 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)

И

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
источник