Яка точність 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

Відповіді:


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) - відносно дуже швидка операція?
Мартін F

Дякую за вашу відповідь, це добре пояснює поведінку. Я фактично працюю над проектом geodjango, тому я буду використовувати __equalsтам фільтр, який, на мою думку, перекладається на функцію ST_Equals.
yellowcap

1
Так, ST_AsBinary швидко. Тести рівності на байті, ймовірно, включають 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працює правильно для даних про точки в ОП:

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)
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.