Nếu bạn kết hợp các câu trả lời cho đến nay, dọn dẹp và cải thiện, bạn sẽ đến truy vấn ưu việt này:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
Đó là nhiều nhanh hơn so với cả hai người. Có hiệu suất của câu trả lời hiện được chấp nhận theo yếu tố 10 - 15 (trong các thử nghiệm của tôi trên PostgreQuery 8.4 và 9.1).
Nhưng điều này vẫn còn xa tối ưu. Sử dụng một NOT EXISTS
(chống) bán tham gia để có hiệu suất thậm chí tốt hơn. EXISTS
là SQL chuẩn, đã tồn tại mãi mãi (ít nhất là từ PostgreSQL 7.2, rất lâu trước khi câu hỏi này được hỏi) và phù hợp hoàn hảo với các yêu cầu được trình bày:
UPDATE sales s
SET status = 'ACTIVE'
WHERE NOT EXISTS (
SELECT FROM sales s1 -- SELECT list can be empty for EXISTS
WHERE s.saleprice = s1.saleprice
AND s.saledate = s1.saledate
AND s.id <> s1.id -- except for row itself
)
AND s.status IS DISTINCT FROM 'ACTIVE'; -- avoid empty updates. see below
db <> fiddle ở đây
Fiddle SQL cũ
Khóa duy nhất để xác định hàng
Nếu bạn không có khóa chính hoặc khóa duy nhất cho bảng ( id
trong ví dụ), bạn có thể thay thế bằng cột hệ thống ctid
cho mục đích của truy vấn này (nhưng không phải cho một số mục đích khác):
AND s1.ctid <> s.ctid
Mỗi bảng nên có một khóa chính. Thêm một nếu bạn chưa có. Tôi đề nghị một serial
hoặc một IDENTITY
cột trong Postgres 10+.
Liên quan:
Làm thế nào là nhanh hơn?
Truy vấn con trong EXISTS
chống bán tham gia có thể ngừng đánh giá ngay khi tìm thấy bản sao đầu tiên (không có ý định tìm kiếm thêm). Đối với một bảng cơ sở với một vài bản sao, điều này chỉ hiệu quả hơn một chút. Với rất nhiều các bản sao này trở thành cách hiệu quả hơn.
Không bao gồm các cập nhật trống
Đối với các hàng đã có status = 'ACTIVE'
bản cập nhật này sẽ không thay đổi bất cứ điều gì, nhưng vẫn chèn một phiên bản hàng mới với chi phí đầy đủ (áp dụng ngoại lệ nhỏ). Thông thường, bạn không muốn điều này. Thêm một WHERE
điều kiện khác như đã trình bày ở trên để tránh điều này và làm cho nó nhanh hơn nữa:
Nếu status
được xác định NOT NULL
, bạn có thể đơn giản hóa thành:
AND status <> 'ACTIVE';
Kiểu dữ liệu của cột phải hỗ trợ <>
toán tử. Một số loại như json
không. Xem:
Sự khác biệt tinh tế trong xử lý NULL
Truy vấn này (không giống như câu trả lời hiện được chấp nhận bởi Joel ) không coi các giá trị NULL là bằng nhau. Hai hàng sau đây (saleprice, saledate)
sẽ đủ điều kiện là "khác biệt" (mặc dù trông giống hệt mắt người):
(123, NULL)
(123, NULL)
Cũng chuyển trong một chỉ mục duy nhất và hầu hết mọi nơi khác, vì các giá trị NULL không so sánh bằng nhau theo tiêu chuẩn SQL. Xem:
OTOH, GROUP BY
, DISTINCT
hoặc DISTINCT ON ()
giá trị điều trị NULL như bằng nhau. Sử dụng một kiểu truy vấn phù hợp tùy thuộc vào những gì bạn muốn đạt được. Bạn vẫn có thể sử dụng truy vấn nhanh hơn này IS NOT DISTINCT FROM
thay vì =
cho bất kỳ hoặc tất cả các so sánh để làm cho NULL so sánh bằng nhau. Hơn:
Nếu tất cả các cột được so sánh được xác định NOT NULL
, không có chỗ cho sự bất đồng.