ความแม่นยำของ 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) เป็นการดำเนินการที่ค่อนข้างเร็วได้หรือไม่
Martin F

ขอบคุณสำหรับคำตอบของคุณนี่อธิบายพฤติกรรมได้ดี จริง ๆ แล้วฉันกำลังทำงานกับโครงการ geodjango ดังนั้นฉันจะใช้__equalsตัวกรองที่นั่นซึ่งแปลเป็นฟังก์ชัน ST_Equals ที่ฉันคิด
yellowcap

1
ใช่ ST_AsBinary รวดเร็ว การทดสอบความเท่าเทียมกันใน bytea อาจเกี่ยวข้องกับ memcmp ซึ่งเป็น op ที่เร็วมากดังนั้นจึงไม่น่ากลัวเกินไป
Paul Ramsey

คุณเสนออะไรที่นี่ @PaulRamsey? SELECT DISTINCT ST_AsBinary(geom)? ที่ให้การแสดงเลขฐานสองของgeomผลลัพธ์ คุณสามารถทำได้SELECT MAX(geom) FROM the_table GROUP BY ST_AsBinary(geom);(ฉันคิดว่าMAX()จำเป็นต้องมีฟังก์ชั่นรวมเช่นเดียวกับในSELECTเนื่องจากส่วนGROUP BYคำสั่งใช้ST_AsBinary()ฟังก์ชันส่งคืนไม่ใช่ฟิลด์ตัวเอง) นั่นดูดีหรือไม่?
Martin Burch

7

ให้คำอธิบายที่ดีเยี่ยมของพอลแรมซีย์ว่าทำไมคำถามต่อไปคือสิ่งที่สามารถทำได้ คุณSELECT DISTINCTใช้ฟิลด์เรขาคณิตอย่างไรและทำงานตามที่คาดไว้ได้อย่างไร

ในคำตอบของ Paul ฉันเสนอให้ใช้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)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.