หากคุณได้รวบรวมคำตอบมารวมกันทำความสะอาดและปรับปรุงคุณจะได้รับแบบสอบถามที่ยอดเยี่ยมนี้:
UPDATE sales
SET status = 'ACTIVE'
WHERE (saleprice, saledate) IN (
SELECT saleprice, saledate
FROM sales
GROUP BY saleprice, saledate
HAVING count(*) = 1
);
ซึ่งเป็นมากเร็วกว่าทั้งของพวกเขา Nukes ประสิทธิภาพของคำตอบที่ได้รับการยอมรับในปัจจุบันโดยปัจจัย 10 - 15 (ในการทดสอบของฉันเกี่ยวกับ PostgreSQL 8.4 และ 9.1)
แต่นี่ยังห่างไกลจากความเหมาะสม ใช้การNOT EXISTSเข้าร่วมกึ่ง (ต่อต้าน) เพื่อประสิทธิภาพที่ดียิ่งขึ้น EXISTSเป็น SQL มาตรฐานได้รับรอบอย่างถาวร (อย่างน้อยตั้งแต่ PostgreSQL 7.2 นานก่อนที่คำถามนี้ถูกถาม) และเหมาะกับความต้องการที่นำเสนออย่างสมบูรณ์แบบ:
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 ที่นี่
Old SQL Fiddle
คีย์ที่ไม่ซ้ำเพื่อระบุแถว
หากคุณไม่มีคีย์หลักหรือคีย์เฉพาะสำหรับตาราง ( idในตัวอย่าง) คุณสามารถแทนที่ด้วยคอลัมน์ระบบctidเพื่อวัตถุประสงค์ในการสืบค้นนี้ (แต่ไม่ใช่เพื่อจุดประสงค์อื่น):
AND s1.ctid <> s.ctid
ทุกตารางควรมีคีย์หลัก เพิ่มหนึ่งถ้าคุณยังไม่มี ผมขอแนะนำให้serialหรือIDENTITYคอลัมน์ใน Postgres 10+
ที่เกี่ยวข้อง:
มันเร็วแค่ไหน?
เคียวรีย่อยในการEXISTSต่อต้านการรวมกึ่งสามารถหยุดการประเมินได้ทันทีที่พบ dupe แรก (ไม่ต้องมองหาอีกต่อไป) สำหรับตารางพื้นฐานที่มีการทำซ้ำสองสามรายการนี้จะมีประสิทธิภาพเพียงเล็กน้อยเท่านั้น ที่มีจำนวนมากที่ซ้ำกันนี้จะกลายเป็นวิธีที่มีประสิทธิภาพมากขึ้น
ไม่รวมการอัปเดตที่ว่างเปล่า
สำหรับแถวที่มีstatus = 'ACTIVE'การอัปเดตนี้แล้วจะไม่เปลี่ยนแปลงอะไรเลย แต่ยังคงแทรกเวอร์ชันแถวใหม่ด้วยค่าใช้จ่ายเต็มรูปแบบ (มีข้อยกเว้นเล็กน้อย) โดยปกติคุณไม่ต้องการสิ่งนี้ เพิ่มWHEREเงื่อนไขอื่นเช่นที่แสดงด้านบนเพื่อหลีกเลี่ยงปัญหานี้และทำให้เร็วขึ้น:
หากstatusมีการกำหนดไว้NOT NULLคุณสามารถทำให้:
AND status <> 'ACTIVE';
ประเภทข้อมูลของคอลัมน์จะต้องสนับสนุน<>ผู้ปฏิบัติงาน บางประเภทjsonไม่ชอบ ดู:
ความแตกต่างเล็กน้อยในการจัดการ NULL
แบบสอบถามนี้ (ต่างจากคำตอบที่ Joel ยอมรับในปัจจุบัน ) ไม่ถือว่าค่า NULL เท่ากัน สองแถวสำหรับต่อไปนี้(saleprice, saledate)จะถือว่าเป็น "ชัดเจน" (แม้ว่าจะดูคล้ายกับดวงตามนุษย์):
(123, NULL)
(123, NULL)
ส่งผ่านไปยังดัชนีที่ไม่ซ้ำกันและเกือบทุกที่อื่นเนื่องจากค่า NULL ไม่เปรียบเทียบเท่ากับตามมาตรฐาน SQL ดู:
OTOH, GROUP BY, DISTINCTหรือDISTINCT ON ()ค่าเป็นศูนย์การรักษาที่เท่าเทียมกัน ใช้สไตล์การสืบค้นที่เหมาะสมขึ้นอยู่กับสิ่งที่คุณต้องการบรรลุ คุณยังสามารถใช้แบบสอบถามที่เร็วกว่านี้IS NOT DISTINCT FROMแทน=การเปรียบเทียบใด ๆ หรือทั้งหมดเพื่อให้ค่า NULL เปรียบเทียบเท่ากัน มากกว่า:
หากมีการกำหนดคอลัมน์ทั้งหมดที่เปรียบเทียบจะNOT NULLไม่มีที่ว่างสำหรับความขัดแย้ง