Độ chính xác của CHỌN DISTINCT trên cột hình học PostGIS là gì?


19

Tôi tự hỏi độ chính xác của SELECT DISTINCTtoán tử là gì trên hình học PostGIS. Trên hệ thống của tôi, truy vấn sau cho tôi tổng số 5, có nghĩa là các điểm được chèn được coi là bằng nhau nếu chúng khác nhau dưới 1e-5 và tôi không chắc đó có phải là một tính năng của PostGIS, một sự cố khi cài đặt của tôi hoặc một lỗi.

Có ai biết nếu đó là hành vi dự kiến?

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)

Tôi đang dùng:

$ 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

trên OSX 10.9

Câu trả lời:


18

Tôi ngạc nhiên vì nó khá thô, nhưng nó đây. Đó không phải là DISTINCT, mỗi se, đó là toán tử '=', được định nghĩa cho hình học là 'đẳng thức của các khóa chỉ số', thực tế có nghĩa là 'sự bình đẳng của các hộp giới hạn 32 bit'.

Bạn có thể thấy hiệu ứng tương tự chỉ bằng cách sử dụng '=' trực tiếp,

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;

Việc tạo '=' hành xử "bằng trực giác" không may sẽ gây ra tổn thất tính toán rất lớn (thực hiện đánh giá ST_Equals () rõ ràng cho lệnh gọi của nhà điều hành) hoặc một số mã phức tạp mới đáng kể (lưu trữ giá trị băm cho hình học lớn hơn, thực hiện các thử nghiệm chính xác khi đang bay những người, chọn đúng đường dẫn mã, v.v.)

Và tất nhiên bây giờ rất nhiều ứng dụng / người dùng đã nội tâm hóa hành vi hiện có, như vậy, vì vậy "cải thiện" nó sẽ là một sự hạ cấp đối với nhiều người. Thay vào đó, bạn có thể thực hiện phân biệt "chính xác" bằng cách tính toán tập hợp của mình trên ST_AsBinary (geom), điều này sẽ thực hiện kiểm tra tính bằng chính xác trên các đầu ra bytea.


Và chúng ta có thể cho rằng ST_AsBinary (geom) là một hoạt động tương đối nhanh không?
Martin F

Cảm ơn câu trả lời của bạn, điều này giải thích hành vi tốt. Tôi thực sự đang làm việc trong một dự án geodjango, vì vậy tôi sẽ sử dụng __equalsbộ lọc ở đó, dịch thành hàm ST_Equals mà tôi nghĩ.
yellowcap

1
Có ST_AsBinary nhanh. Các bài kiểm tra bình đẳng trên bytea có lẽ liên quan đến memcmp, một op rất nhanh, vì vậy không nên quá khủng khiếp.
Paul Ramsey

Bạn đang đề xuất gì ở đây, @PaulRamsey? SELECT DISTINCT ST_AsBinary(geom)? Điều đó mang lại một đại diện nhị phân geomnhư là kết quả. Bạn có thể làm SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);(tôi nghĩ rằng một hàm tổng hợp giống như MAX()được yêu cầu trong mệnh đề SELECTGROUP BYmệnh đề đang sử dụng ST_AsBinary()hàm trả về chứ không phải chính trường đó.) Điều đó có tốt không?
Martin Burch

7

Đưa ra lời giải thích tuyệt vời của Paul Ramsey về lý do tại sao câu hỏi tiếp theo là những gì có thể được thực hiện về nó. Làm thế nào để bạn SELECT DISTINCTtrên các lĩnh vực hình học và có nó thực hiện như mong đợi?

Trong câu trả lời của Paul, tôi đã đề xuất sử dụng SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);nhưng MAX()chậm, rõ ràng cần phải quét bảng.

Thay vào đó, tôi thấy điều này nhanh hơn:

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

4

Chỉ là một bản cập nhật, cho PostGIS 2.4, SELECT DISTINCThoạt động chính xác cho dữ liệu điểm trong 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.