Dữ liệu mẫu được cung cấp:
create table results ( commandid integer primary key);
insert into results (commandid) select * from generate_series(1,1000);
delete from results where random() < 0.20;
Điều này hoạt động:
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE NOT EXISTS (SELECT 1 FROM results WHERE commandid = s.i);
cũng như công thức thay thế này:
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
LEFT OUTER JOIN results ON (results.commandid = s.i)
WHERE results.commandid IS NULL;
Cả hai điều trên dường như dẫn đến các kế hoạch truy vấn giống hệt nhau trong các thử nghiệm của tôi, nhưng bạn nên so sánh với dữ liệu trên cơ sở dữ liệu của mình bằng cách sử dụng EXPLAIN ANALYZE
để xem cái nào là tốt nhất.
Giải thích
Lưu ý rằng thay vì NOT IN
Tôi đã sử dụng NOT EXISTS
với một truy vấn con trong một công thức và một OUTER JOIN
thông thường trong cái khác. Máy chủ DB tối ưu hóa những điều này dễ dàng hơn nhiều và nó tránh được các vấn đề khó hiểu có thể phát sinh với NULL
s trong NOT IN
.
Ban đầu tôi thích OUTER JOIN
công thức, nhưng ít nhất trong 9.1 với dữ liệu thử nghiệm của tôi, NOT EXISTS
biểu mẫu tối ưu hóa cho cùng một kế hoạch.
Cả hai sẽ hoạt động tốt hơn NOT IN
công thức bên dưới khi chuỗi lớn, như trong trường hợp của bạn. NOT IN
được sử dụng để yêu cầu Pg thực hiện tìm kiếm tuyến tính đối với IN
danh sách cho mọi tuple đang được kiểm tra, nhưng việc kiểm tra kế hoạch truy vấn cho thấy Pg có thể đủ thông minh để băm nó ngay bây giờ. NOT EXISTS
(được chuyển thành JOIN
bởi công cụ lập kế hoạch truy vấn) và JOIN
hoạt động tốt hơn.
NOT IN
công thức vừa gây nhầm lẫn khi có NULL commandid
s và có thể không hiệu quả:
SELECT s.i AS missing_cmd
FROM generate_series(0,1000) s(i)
WHERE s.i NOT IN (SELECT commandid FROM results);
vì vậy tôi muốn tránh nó. Với 1.000.000 hàng, hai hàng còn lại hoàn thành trong 1,2 giây và NOT IN
công thức chạy bị ràng buộc bởi CPU cho đến khi tôi cảm thấy nhàm chán và hủy bỏ nó.