Xóa hình học trùng lặp trong bảng postgis


11

Sau - Tôi không biết chuyện gì xảy ra - tất cả các mục trong bảng PostGIS của tôi được nhân đôi! Tôi đã thử cách này để xóa chúng nhưng nó không xóa bất kỳ / tất cả các bản sao:

DELETE FROM planet_osm_point
       WHERE osm_id NOT IN (SELECT min(osm_id)
                        FROM planet_osm_point
                        GROUP BY osm_id)

hoặc này:

DELETE FROM planet_osm_point
WHERE osm_id NOT IN (
    select max(dup.osm_id)
    from planet_osm_point as dup
    group by way);

BIÊN TẬP:

Cuối cùng tôi đã tìm thấy một cách dễ dàng, đó là làm việc trong trường hợp của tôi:

DELETE FROM planet_osm_point WHERE ctid NOT IN
(SELECT max(ctid) FROM planet_osm_point GROUP BY osm_id);

được tìm thấy trên trang này: http://technobytz.com/ most-usiously-postgresql-commands.html


1
Bạn có thể vui lòng cung cấp planet_osm_pointcấu trúc bảng hiện tại ? có nghĩa là loại cột. Bạn có thể viết mã Python cơ bản để thu thập các cột đã chọn, nếu gặp khó khăn với các hàm SQL.
Zia

Vâng, điều đó sẽ hoạt động, nếu bạn có một id (ctid) khác không bị trùng lặp. Tôi đã cho rằng mọi thứ đều giống hệt nhau và được nhân đôi hai lần.
John Powell

Xin lỗi, nhưng tôi đã không có được ctidphương pháp này . Cột này đã được thêm thủ công sau sự kiện sao chép?
Zia

1
"Cột 'CTID' là một cột đặc biệt có sẵn cho mỗi bảng nhưng không nhìn thấy được trừ khi được nêu Giá trị cột CTID được coi là duy nhất cho mỗi hàng trong một bảng -.." Technobytz.com/most-useful-postgresql-commands.html
MAP

Câu trả lời:


20

Một cách để làm điều này là sử dụng chức năng cửa sổ và phân vùng theo hình học, sao cho mỗi hình học lặp lại sẽ có một id: 1, 2, 3, v.v (hoặc 1, 2) trong trường hợp của bạn, và sau đó bạn chỉ cần chọn từ bảng trong đó id = 1, để lấy lại một bộ giá trị (thuộc tính và hình học) duy nhất, ví dụ:

WITH unique_geoms (id, geom) as 
 (SELECT row_number() OVER (PARTITION BY ST_AsBinary(geom)) AS id, geom FROM some_table)
SELECT geom 
FROM unique_geoms 
WHERE id=1;

Rõ ràng, bạn cũng cần thêm các cột osm khác trong phần chọn, đây chỉ là để minh họa, nhưng về cơ bản giống như nhóm theo hình học và chỉ chọn ví dụ đầu tiên của mỗi cột. Lưu ý, bạn cần sử dụng ST_AsBinary trong Phân vùng Bởi vì nếu không thì việc so sánh được thực hiện trên hộp giới hạn, không phải hình học thực tế.

Vì tất cả các thuộc tính khác có lẽ giống nhau cho mỗi cặp hình học, bạn sẽ làm như vậy cho tất cả các trường khác, bao gồm osm_id và để thực sự tạo một bảng mới, duy nhất:

CREATE TABLE osm_unique AS
 WITH unique_geoms (id, osm_id, attr1, attr2,... attrn, geom) AS 
  (SELECT row_number() OVER (PARTITION BY ST_AsBinary(geom)) AS id, osm_id, attr1, attr2,... attrn, geom 
    FROM osm_planet_point)
 SELECT osm_id, attr1, attr2,... attrn, geom 
 FROM unique_geoms 
 WHERE id=1;

Điều này có thể nhanh hơn việc xóa khỏi một bảng hiện có, đặc biệt là nếu có rất nhiều chỉ mục được đặt ra.

CHỈNH SỬA . Viết lại để dễ đọc, nhưng, để lại tín dụng cho dbaston để thu hút sự chú ý của tôi đến ST_AsBinary (geom)


Cảm ơn. Tôi đã thực hiện một lưu ý. Nhưng ví dụ, hãy xem xét kịch bản này khi có một điểm địa lý vừa là điểm dừng xe buýt vừa là điểm giao nhau (không xem xét dữ liệu OSM). Sau đó, chúng ta sẽ có hai geom giống hệt nhau đại diện cho hai tính năng này. Sử dụng phương pháp của bạn sẽ loại bỏ một trong các tính năng. Điều tôi đang nói là làm thế nào để giải quyết vấn đề này khi không có cột cụ thể Partition By?
Zia

1
Xin chào Zia, sau đó bạn phân vùng theo (geom, thuộc tính), để cả hai phải giống nhau để có cùng id. Trong ví dụ của bạn, geom sẽ giống nhau, thuộc tính thì không, vì vậy row_number () sẽ trả về 1 cho cả hai.
John Powell

1
Điều này hiện xác định hình học riêng biệt với hộp giới hạn được chia sẻ dưới dạng trùng lặp (do PARTITION BYsử dụng =toán tử, hoạt động trên phương trình hộp giới hạn). Tôi muốn đề nghị thay đổi ở trên để PARTITION BY ST_AsBinary(geom)sửa chữa.
dbaston

Tôi nghĩ bạn nên chấp nhận câu trả lời này hoặc nêu cách nó không trả lời câu hỏi.
John Powell

1
@AndreSilva. Làm xong. Tôi luôn lo lắng về việc thay đổi câu trả lời mà không làm cho nó rõ ràng đã có một chỉnh sửa. Nhưng, bạn đã đúng, điều này dễ đọc hơn nhiều.
John Powell

2

Đây là một phương pháp khác mà tôi đã sử dụng để loại bỏ các bản sao khỏi tải xuống dữ liệu đất SSURGO. Các shapefiles đã tải xuống không có khóa duy nhất, do đó, cột pk nối tiếp đã được tạo khi tôi nhập vào PostGIS. Có một vài sự trùng lặp trong các tập dữ liệu và tôi đã vô tình nhập một số bản ghi nhiều lần trong khi phát triển tập lệnh nhập.

Nhóm theo câu lệnh bao gồm tất cả các cột trong bảng, ngoại trừ khóa chính.

Nó sẽ chỉ xóa một tập hợp các hàng trùng lặp mỗi lần nó chạy, vì vậy nếu một hàng được lặp lại 4 lần, bạn sẽ cần chạy tối thiểu 3 lần. Điều này có thể không nhanh như giải pháp của John, nhưng hoạt động trong một bảng hiện có. Nó cũng hoạt động khi bạn không có id duy nhất cho mỗi hình học duy nhất (chẳng hạn như osm_id trong câu hỏi ban đầu).

Tôi đã sử dụng một kịch bản python để lặp lại cho đến khi các bản sao đã biến mất, và sau đó chạy hoàn toàn chân không. Tôi nghĩ rằng kịch bản và chân không mỗi lần mất khoảng 30 phút cho vài trăm nghìn bản sao từ khoảng 1,5 triệu bản ghi trong 6 bảng. Rất nhiều cho một lần. Nó đi qua những cái bàn nhỏ rất nhanh.

DELETE FROM schema.table 
  WHERE primary_key IN
    (SELECT MAX(primary_key)
     FROM schema.table 
     GROUP BY ST_AsBinary(geom), col_1, col_2, col_etc
     HAVING COUNT(primary_key) > 1);

EDIT: SQL đã sửa đổi để tránh chạy nhiều lần dựa trên đề xuất @dbaston (bên dưới). Tôi đã thử phương pháp truy vấn này trên một bảng lớn (~ 1,5 triệu bản ghi, ~ 25.000 hàng điểm trùng lặp) và sau khi nó chạy trong 45 phút, tôi đã hủy thực thi. Chạy với SQL ở trên (sử dụng truy vấn con nhỏ hơn từ HAVING COUNT) đã giảm mỗi lần chạy xuống dưới 30 giây. Sau khi chạy 3 lần, nó đã được thực hiện với tất cả các bản sao. SQL dưới đây sẽ ổn cho các bảng nhỏ.

DELETE FROM schema.table 
  WHERE primary_key NOT IN
    (SELECT MAX(primary_key)
     FROM schema.table 
     GROUP BY ST_AsBinary(geom), col_1, col_2, col_etc);

1
Nếu bạn không có khóa chính, bạn có thể sử dụng ctidcột luôn có sẵn (xem tài liệu ).
dbaston

1
Bạn có thể tránh chạy nó nhiều lần bằng cách kiểm traprimary_key NOT IN (SELECT max(primary_key) ....
dbaston

@dbaston Tôi đã ghi chú trong câu trả lời ở trên. Loại bỏ HAVING COUNT làm tăng đáng kể kích thước của kết quả truy vấn con và do đó, số lượng so sánh mà câu lệnh xóa cần thực hiện. Tôi đã ngạc nhiên về việc thực hiện lâu hơn trên một cái bàn lớn.
Nate Wanner

@NateWanner KHÔNG EXISTS có thể cung cấp cho bạn một số tốc độ bổ sung trong trường hợp này.
Michal Zimmermann

@MichalZimmermann Tôi không chắc chắn tôi sẽ theo dõi bạn - cả hai phiên bản đều mong muốn truy vấn con trả về kết quả.
Nate Wanner

1

Một câu trả lời tổng quát hơn để dễ dàng xóa các bản sao hình học trong bảng PostGIS. Lệnh sau sẽ xóa tất cả các tính năng có hình dạng trùng lặp trong "tên_bảng" dựa trên khóa chính (cột "gid") và đẳng thức của hình học (cột "geom"). Hãy nhận biết nó thực sự xóa tất cả các bản sao hình học, chúng sẽ biến mất, mãi mãi! Có thể sao lưu trước?

DELETE FROM schema_name.table_name a
    USING schema_name.table_name b 
WHERE a.gid > b.gid AND st_equals(a.geom, b.geom);
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.