2010-04-21 44 views
33

Tôi có một bảng có khoảng 5 triệu hàng có ràng buộc fk tham chiếu khóa chính của bảng khác (cũng xấp xỉ 5 triệu hàng).PostgreSQL - vô hiệu hóa các ràng buộc

Tôi cần xóa khoảng 75000 hàng từ cả hai bảng. Tôi biết rằng nếu tôi cố gắng làm điều này với ràng buộc fk cho phép nó sẽ mất một khoảng thời gian không thể chấp nhận được.

Xuất phát từ nền tảng Oracle ý tưởng đầu tiên của tôi là vô hiệu hóa ràng buộc, hãy xóa & sau đó bật lại ràng buộc. PostGres xuất hiện để cho phép tôi vô hiệu hóa các trình kích hoạt ràng buộc nếu tôi là người dùng siêu (tôi không, nhưng tôi đăng nhập với tư cách là người dùng sở hữu/tạo các đối tượng) nhưng điều đó dường như không hoàn toàn là những gì tôi muốn.

Tùy chọn khác là xóa ràng buộc và sau đó khôi phục nó. Tôi lo rằng việc xây dựng lại ràng buộc sẽ mất thời gian cho kích thước của các bảng của tôi.

Mọi suy nghĩ?

chỉnh sửa: sau khi khuyến khích của Billy, tôi đã thử thực hiện xóa mà không thay đổi bất kỳ ràng buộc nào và mất hơn 10 phút. Tuy nhiên, tôi đã phát hiện ra rằng bảng mà từ đó tôi đang cố gắng để xóa có một khóa tự tham chiếu nước ngoài ... trùng lặp (& không được lập chỉ mục).

Bản cập nhật cuối cùng - Tôi đã bỏ khóa tự tham chiếu ngoài, đã xóa và thêm nó vào. Billy phải tròn nhưng tiếc là tôi không thể chấp nhận nhận xét của anh ấy làm câu trả lời!

+4

Nếu nó dùng lâu như vậy, thậm chí với 5 triệu hàng, sau đó bạn có một cái gì đó thiết lập sai. –

+0

Cái gì? Xóa hoặc mở lại ràng buộc? Và có, nó là khá có thể một cái gì đó (s) được thiết lập sai hoặc theo một cách tối ưu hóa ít hơn - cơ sở dữ liệu đã khá nhiều được 'xây dựng' bởi hibernate (tôi không có gì để làm với điều đó). – azp74

+10

Xóa. Kiểm tra FK từ các bảng được lập chỉ mục có thời gian tuyến tính và loại bỏ 75000 + 75000 hàng = 150 000 hàng. Xem xét một trường hợp xấu nhất 19 so sánh cho mỗi kiểm tra FK (tìm kiếm nhị phân, lg (5 triệu) == 19), và có lẽ 20 so sánh máy mỗi so sánh hàng, bằng 57 000 000 so sánh. Xem xét một ước tính bảo thủ của máy trung bình có thể làm một tỷ so sánh một giây, dễ dàng, điều này vẫn sẽ mất ít hơn một giây của thời gian CPU. Tải từ đĩa cũng không phải là một vấn đề lớn bởi vì ngay cả ở 5 triệu hàng bảng nên phù hợp với RAM. –

Trả lời

42

Mỗi nhận xét trước đó, nó sẽ là một vấn đề. Điều đó nói rằng, có một lệnh có thể là những gì bạn đang tìm kiếm - nó sẽ thiết lập các ràng buộc để trì hoãn để chúng được kiểm tra trên COMMIT, không phải trên mọi xóa. Nếu bạn đang làm chỉ một DELETE lớn của tất cả các hàng, nó sẽ không tạo ra sự khác biệt, nhưng nếu bạn đang làm nó theo từng phần, nó sẽ.

SET CONSTRAINTS ALL DEFERRED 

là những gì bạn đang tìm kiếm trong trường hợp đó. Lưu ý rằng các ràng buộc phải được đánh dấu là DEFERRABLE trước khi chúng có thể được hoãn lại. Ví dụ:

ALTER TABLE table_name 
    ADD CONSTRAINT constraint_uk UNIQUE(column_1, column_2) 
    DEFERRABLE INITIALLY IMMEDIATE; 

Ràng buộc sau đó có thể được hoãn lại trong một giao dịch hoặc chức năng như sau:

CREATE OR REPLACE FUNCTION f() RETURNS void AS 
$BODY$ 
BEGIN 
    SET CONSTRAINTS ALL DEFERRED; 

    -- Code that temporarily violates the constraint... 
    -- UPDATE table_name ... 
END; 
$BODY$ 
    LANGUAGE plpgsql VOLATILE 
    COST 100; 
+1

Chắc chắn đáng để thử, nhưng tôi không tin rằng các ràng buộc trì hoãn nhanh hơn. AFAIK họ chỉ thay đổi công việc xác nhận từ DELETE-time thành COMMIT-time. – intgr

+1

Tôi đã có thể đưa ra một đi nhưng giảm fk và phục hồi nó làm việc. Giống như intgr, tôi tự hỏi nếu nó sẽ không chỉ thay đổi việc kiểm tra của fk để cam kết thời gian vì vậy tôi chắc chắn sẽ nhớ nó cho thời gian tới. – azp74

+1

Tôi đã bỏ một cơ sở dữ liệu và nhập lại nó sau khi chạy 'SET CONSTRAINTS ALL DEFERRED'. Có cách nào để "kích hoạt lại" các ràng buộc này sau khi nhập xong không? Đó là một tập tin khá lớn, do đó sẽ rất khó để sắp xếp lại việc tạo bảng. Tôi đã nhận được xung quanh này trước khi nhập dữ liệu hai lần. – taco

-7

Disable tất cả các ràng bảng

ALTER TABLE TableName NOCHECK CONSTRAINT ConstraintName 

- Enable tất cả các ràng bảng

ALTER TABLE TableName CHECK CONSTRAINT ConstraintName 
+3

Câu hỏi đã được về Postgresql mà không có khả năng đó (như của v9.4). –

+0

Đồng ý v9.4 không có tính năng này LRI: lỗi cú pháp tại hoặc gần "NOCHECK" LINE 1: ALTER TABLE Bảng tên NOCHECK CONSTRAINT ConstraintName –

3

(Câu trả lời này giả định ý định của bạn là xóa tất cả các hàng của các bảng này, không chỉ là lựa chọn.)

Tôi cũng phải làm điều này, nhưng là một phần của bộ thử nghiệm. Tôi đã tìm thấy câu trả lời, được đề xuất elsewhere on SO.Sử dụng TRUNCATE TABLE như sau:

TRUNCATE TABLE <list-of-table-names> [RESTART IDENTITY] [CASCADE]; 

Sau đây nhanh chóng xóa tất cả các hàng từ bảng table1, table2, và table3, với điều kiện là không có tài liệu tham khảo để hàng trong các bảng từ các bảng không được liệt kê:

TRUNCATE TABLE table1, table2, table3; 

Miễn là tài liệu tham khảo nằm giữa các bảng được liệt kê, PostgreSQL sẽ xóa tất cả các hàng mà không cần quan tâm đến tính toàn vẹn tham chiếu. Nếu một bảng khác với các bảng được liệt kê tham chiếu một hàng của một trong các bảng này, truy vấn sẽ thất bại.

Tuy nhiên, bạn có thể đủ điều kiện truy vấn để nó cũng truncates tất cả các bảng với tham chiếu đến các bảng niêm yết (mặc dù tôi đã không cố gắng này):

TRUNCATE TABLE table1, table2, table3 CASCADE; 

Theo mặc định, trình tự của các bảng này không khởi động lại đánh số. Các hàng mới sẽ tiếp tục với số thứ tự tiếp theo. Để khởi động lại trình tự đánh số:

TRUNCATE TABLE table1, table2, table3 RESTART IDENTITY; 
7

gì làm việc cho tôi là để vô hiệu hóa từng cái một sự TRIGGERS những bảng được sẽ được tham gia vào các hoạt động DELETE.

ALTER TABLE reference DISABLE TRIGGER ALL; 
DELETE FROM reference WHERE refered_id > 1; 
ALTER TABLE reference ENABLE TRIGGER ALL; 

Giải pháp đang hoạt động trong phiên bản 9.3.16. Trong trường hợp của tôi thời gian đã đi từ 45 phút đến 14 giây thực hiện các hoạt động DELETE.

Như đã nêu trong phần nhận xét của @amphetamachine, bạn sẽ cần có các đặc quyền admin cho các bảng để thực hiện tác vụ này.

+1

Lưu ý rằng người dùng PostgreSQL thực hiện lệnh 'ALTER TABLE' phải là chủ sở hữu của bàn. – amphetamachine

0

Nếu bạn cố gắng DISABLE TRIGGER ALL và nhận được một lỗi như permission denied: "RI_ConstraintTrigger_a_16428" is a system trigger (Tôi đã nhận này trên Amazon RDS), hãy thử này:

set session_replication_role to replica; 

Nếu đây thành công, tất cả các trigger làm nền tảng cho chế bảng sẽ bị vô hiệu. Bây giờ, tùy thuộc vào bạn để đảm bảo các thay đổi của bạn rời khỏi DB ở trạng thái nhất quán!

Sau đó, khi bạn làm xong, bật lại tính gây & hạn chế cho phiên của bạn với:

set session_replication_role to default; 
Các vấn đề liên quan