Cách hiệu quả nhất để xóa hàng loạt từ postgres


23

Tôi đang tự hỏi cách hiệu quả nhất sẽ là xóa số lượng lớn các hàng khỏi PostgreSQL, quá trình này sẽ là một phần của nhiệm vụ định kỳ mỗi ngày để nhập dữ liệu hàng loạt (một loạt các phần chèn + xóa) vào một bảng. Có thể có hàng ngàn, có khả năng hàng triệu hàng cần xóa.

Tôi có một tập tin các khóa chính, mỗi khóa một dòng. Hai tùy chọn mà tôi đã nghĩ đến nằm dọc theo các dòng dưới đây, nhưng tôi không biết / hiểu đủ về các phần bên trong của PostgreQuery để đưa ra quyết định sáng suốt nhất.

  • Thực hiện một DELETEtruy vấn cho mỗi hàng trong tệp, với một khóa đơn giản WHEREtrên khóa chính (hoặc nhóm các lần xóa trong các đợt nsử dụng một IN()mệnh đề)
  • Nhập khóa chính vào bảng tạm thời bằng COPYlệnh và sau đó xóa khỏi bảng chính bằng cách sử dụng phép nối

Bất kỳ đề xuất sẽ được nhiều đánh giá cao!


1
Câu hỏi tương tự đã được trả lời chi tiết hơn ở đây: stackoverflow.com/a/8290958
Simon

Câu trả lời:


25

Tùy chọn thứ hai của bạn sạch hơn rất nhiều và sẽ hoạt động đủ tốt để làm cho nó xứng đáng. Thay thế của bạn là xây dựng các truy vấn khổng lồ sẽ khá khó khăn để lập kế hoạch và thực hiện. Nói chung, bạn sẽ tốt hơn nếu để PostgreSQL thực hiện công việc ở đây. Nói chung, tôi đã tìm thấy các bản cập nhật trên hàng chục ngàn hàng theo cách bạn đang mô tả để thực hiện đầy đủ, nhưng có một điều quan trọng cần tránh làm.

Cách để làm điều đó là sử dụng một lựa chọn và tham gia xóa của bạn.

DELETE FROM foo WHERE id IN (select id from rows_to_delete);

Trong mọi trường hợp, bạn nên như sau với một bảng lớn:

DELETE FROM foo WHERE id NOT IN (select id from rows_to_keep);

Điều này thường sẽ gây ra một antijoin vòng lồng nhau sẽ làm cho hiệu suất khá khó khăn. Nếu bạn cuối cùng phải đi theo con đường đó, hãy làm điều này thay vào đó:

DELETE FROM foo 
WHERE id IN (select id from foo f 
          LEFT JOIN rows_to_keep d on f.id = d.id
              WHERE d.id IS NULL);

PostgreSQL thường khá giỏi trong việc tránh các kế hoạch xấu nhưng vẫn có những trường hợp liên quan đến các liên kết bên ngoài có thể tạo ra sự khác biệt lớn giữa các kế hoạch tốt và xấu.

Điều này đang đi lang thang xa hơn một chút, nhưng tôi cho rằng nó đáng được đề cập bởi vì nó dễ dàng đi từ IN đến KHÔNG VÀO và xem bể hiệu năng truy vấn.


Điều đó đã giúp rất nhiều, cảm ơn! Tuy nhiên tôi thấy rằng sử dụng "kết hợp truy vấn" sẽ hiệu quả hơn trong trường hợp cụ thể này. Ví dụ: IN ( select id from foo except select id from rows_to_keep ) Xem postgresql.org/docs/9.4/static/queries-union.html
Ufos

1

Tôi đã gặp câu hỏi này bởi vì tôi có một vấn đề tương tự. Tôi đang dọn dẹp một cơ sở dữ liệu có 300M + hàng, cơ sở dữ liệu cuối cùng sẽ chỉ có khoảng 30% dữ liệu gốc. Nếu bạn đang phải đối mặt với một kịch bản tương tự, thực sự dễ dàng hơn để chèn vào một bảng mới và lập chỉ mục lại thay vì xóa.

Làm một cái gì đó như

CREATE temp_foo as SELECT * FROM foo WHERE 1=2;
INSERT INTO temp_foo (SELECT * FROM foo where foo.id IN (SELECT bar.id FROM BAR);

Với việc lập chỉ mục thích hợp trên foo và bar, bạn có thể tránh quét Seq.

Sau đó, bạn sẽ phải lập chỉ mục lại và đổi tên bảng.

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.