หากคุณได้รวบรวมคำตอบมารวมกันทำความสะอาดและปรับปรุงคุณจะได้รับแบบสอบถามที่ยอดเยี่ยมนี้:
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
ไม่มีที่ว่างสำหรับความขัดแย้ง