Jika Anda mengumpulkan jawaban sejauh ini, bersihkan dan tingkatkan, Anda akan sampai pada pertanyaan superior ini:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
Yang jauh lebih cepat daripada keduanya. Nukes kinerja jawaban yang saat ini diterima oleh faktor 10 - 15 (dalam tes saya pada PostgreSQL 8.4 dan 9.1).
Namun ini masih jauh dari optimal. Gunakan NOT EXISTS
semi-join (anti-) untuk kinerja yang lebih baik. EXISTS
adalah SQL standar, telah ada selamanya (setidaknya sejak PostgreSQL 7.2, jauh sebelum pertanyaan ini diajukan) dan sangat cocok dengan persyaratan yang disajikan:
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 <> biola di sini
Old SQL Fiddle
Kunci unik untuk mengidentifikasi baris
Jika Anda tidak memiliki kunci utama atau unik untuk tabel ( id
dalam contoh), Anda bisa mengganti dengan kolom sistem ctid
untuk tujuan permintaan ini (tetapi tidak untuk beberapa tujuan lain):
AND s1.ctid <> s.ctid
Setiap tabel harus memiliki kunci utama. Tambahkan satu jika Anda belum memilikinya. Saya menyarankan satu serial
atau satu IDENTITY
kolom di Postgres 10+.
Terkait:
Bagaimana ini lebih cepat?
Subquery di EXISTS
anti-semi-join dapat berhenti mengevaluasi begitu dupe pertama ditemukan (tidak ada gunanya mencari lebih lanjut). Untuk tabel dasar dengan beberapa duplikat, ini hanya sedikit lebih efisien. Dengan banyak duplikat ini menjadi jauh lebih efisien.
Kecualikan pembaruan kosong
Untuk baris yang sudah memiliki status = 'ACTIVE'
pembaruan ini tidak akan mengubah apa pun, tetapi tetap memasukkan versi baris baru dengan biaya penuh (pengecualian kecil berlaku). Biasanya, Anda tidak menginginkan ini. Tambahkan WHERE
kondisi lain seperti yang ditunjukkan di atas untuk menghindari ini dan membuatnya lebih cepat:
Jika status
didefinisikan NOT NULL
, Anda dapat menyederhanakan untuk:
AND status <> 'ACTIVE';
Jenis data kolom harus mendukung <>
operator. Beberapa tipe suka json
tidak. Lihat:
Perbedaan yang halus dalam penanganan NULL
Kueri ini (tidak seperti jawaban yang saat ini diterima oleh Joel ) tidak memperlakukan nilai NULL sebagai sama. Dua baris berikut untuk (saleprice, saledate)
dikualifikasikan sebagai "berbeda" (meskipun terlihat identik dengan mata manusia):
(123, NULL)
(123, NULL)
Juga melewati dalam indeks unik dan hampir di tempat lain, karena nilai NULL tidak membandingkan sama dengan standar SQL. Lihat:
Otoh, GROUP BY
, DISTINCT
atau DISTINCT ON ()
nilai-nilai memperlakukan NULL sebagai sama. Gunakan gaya permintaan yang sesuai tergantung pada apa yang ingin Anda capai. Anda masih dapat menggunakan kueri yang lebih cepat ini dengan IS NOT DISTINCT FROM
alih - alih =
untuk setiap atau semua perbandingan untuk membuat NULL membandingkannya. Lebih:
Jika semua kolom yang dibandingkan didefinisikan NOT NULL
, tidak ada ruang untuk ketidaksepakatan.