วิธีค้นหาระเบียนที่ซ้ำกันใน PostgreSQL


190

ฉันมีตารางฐานข้อมูล PostgreSQL ชื่อ "user_links" ซึ่งปัจจุบันอนุญาตให้มีฟิลด์ที่ซ้ำกันต่อไปนี้:

year, user_id, sid, cid

ข้อ จำกัด ที่ไม่ซ้ำกันในขณะนี้คือสนามแรกที่เรียกว่า "id" แต่ตอนนี้ฉันต้องการเพิ่มข้อ จำกัด เพื่อให้แน่ใจว่าyear, user_id, sidและcidทุกคนที่ไม่ซ้ำกัน แต่ฉันไม่สามารถใช้ข้อ จำกัด เพราะค่าที่ซ้ำกันอยู่แล้วที่ละเมิดข้อ จำกัด นี้

มีวิธีการค้นหารายการที่ซ้ำกันทั้งหมดหรือไม่


2
ซ้ำซ้อนเป็นไปได้ของค้นหาแถวที่ซ้ำกันด้วย PostgreSQL
drs

คำตอบ:


335

แนวคิดพื้นฐานจะใช้การสืบค้นแบบซ้อนที่มีการรวมการนับ:

select * from yourTable ou
where (select count(*) from yourTable inr
where inr.sid = ou.sid) > 1

คุณสามารถปรับที่ส่วนคำสั่งในแบบสอบถามภายในเพื่อ จำกัด การค้นหา


มีอีกวิธีที่ดีสำหรับสิ่งที่กล่าวถึงในความคิดเห็น (แต่ไม่ใช่ทุกคนที่อ่านพวกเขา):

select Column1, Column2, count(*)
from yourTable
group by Column1, Column2
HAVING count(*) > 1

หรือสั้นกว่า:

SELECT (yourTable.*)::text, count(*)
FROM yourTable
GROUP BY yourTable.*
HAVING count(*) > 1

65
คุณสามารถใช้ HAVING:select co1, col2, count(*) from tbl group by col1, col2 HAVING count(*)>1
alexkovelsky

1
ขอบคุณ @alexkovelsky การมีคำสั่งง่ายกว่าที่จะแก้ไขให้ฉันและวิ่งเร็วขึ้น ฉันขอแนะนำคำตอบเพื่อความชัดเจนยิ่งขึ้น
Vesanto

ตัวเลือกเหล่านี้ทำงานกับฉันคนอื่นจัดกลุ่มผลลัพธ์และตัวเลือกเหล่านี้ให้ฉันระเบียนที่ซ้ำกันทั้งหมดแทนที่จะเป็นเพียงการทำซ้ำบันทึกขอบคุณ!
rome3ro

1
ฉันมีคำตอบของคุณช้าไปหน่อย บนตารางขนาด 10k แถว * 18 คอลัมน์ข้อความค้นหาใช้เวลา 8 วินาที
สิ้นสุด

1
พี่นั่นติดขัดตรงนั้น เฮ้ใช่ ขอบคุณ 💯
dps

91

จาก " ค้นหาแถวที่ซ้ำกันด้วย PostgreSQL " นี่คือโซลูชันอัจฉริยะ:

select * from (
  SELECT id,
  ROW_NUMBER() OVER(PARTITION BY column1, column2 ORDER BY id asc) AS Row
  FROM tbl
) dups
where 
dups.Row > 1

11
นี่มันเร็วมาก! ทำงานมากกว่าล้านแถวในเสี้ยววินาที คำตอบอื่น ๆ เพียงแค่แขวนอยู่ที่นั่น ...
dmvianna

5
อย่างที่ฉันเห็นแบบสอบถามนี้ไม่ได้พิจารณาแถวทั้งหมดภายในกลุ่ม มันแสดงให้เห็นเพียงซ้ำกับสิ่งที่เป็นส่วนหนึ่งของรายการที่ซ้ำกันจะอยู่กับ rownum = 1. ผมที่ถูกต้องถ้าฉันผิด
วลาดิเมีย Filipchenko

9
@ vladimir Filipchenko หากต้องการได้ทุกบรรทัดเพิ่มระดับการแก้ปัญหาของ Alexkovelsky:SELECT * FROM ( SELECT *, LEAD(row,1) OVER () AS nextrow FROM ( SELECT *, ROW_NUMBER() OVER(w) AS row FROM tbl WINDOW w AS (PARTITION BY col1, col2 ORDER BY col3) ) x ) y WHERE row > 1 OR nextrow > 1;
Le Droid

4
@VladimirFilipchenko เพียงแทนที่ROW_NUMBER()ด้วยCOUNT(*)และเพิ่มrows between unbounded preceding and unbounded followingหลังจากORDER BY id asc
alexkovelsky

2
ดีกว่าโซลูชันอื่น ๆ ที่ฉันเคยพบมามาก ยังทำงานได้ดีพอ ๆ กันสำหรับการลบงานที่ซ้ำซ้อนด้วยDELETE ...USINGและการปรับเปลี่ยนเล็กน้อย
Brandon

6

คุณสามารถเข้าร่วมในตารางเดียวกันในฟิลด์ที่จะทำซ้ำและต่อต้านการเข้าร่วมในฟิลด์ ID เลือกฟิลด์ id จากนามแฝงของตารางแรก (tn1) จากนั้นใช้ฟังก์ชัน array_agg บนฟิลด์ id ของนามแฝงตารางที่สอง ในที่สุดเพื่อให้ฟังก์ชัน array_agg ทำงานอย่างถูกต้องคุณจะจัดกลุ่มผลลัพธ์ตามฟิลด์ tn1.id สิ่งนี้จะสร้างชุดผลลัพธ์ที่มี id ของเร็กคอร์ดและอาร์เรย์ของ id ทั้งหมดที่เหมาะสมกับเงื่อนไขการรวม

select tn1.id,
       array_agg(tn2.id) as duplicate_entries, 
from table_name tn1 join table_name tn2 on 
    tn1.year = tn2.year 
    and tn1.sid = tn2.sid 
    and tn1.user_id = tn2.user_id 
    and tn1.cid = tn2.cid
    and tn1.id <> tn2.id
group by tn1.id;

เห็นได้ชัดว่ารหัสที่จะอยู่ในอาเรย์ซ้ำซ้อนสำหรับรหัสเดียวก็จะมีรายการของตัวเองในชุดผลลัพธ์ คุณจะต้องใช้ชุดผลลัพธ์นี้เพื่อตัดสินใจว่า id ใดที่คุณต้องการให้กลายเป็น 'ความจริง' ระเบียนเดียวที่ไม่ควรลบ บางทีคุณอาจทำสิ่งนี้:

with dupe_set as (
select tn1.id,
       array_agg(tn2.id) as duplicate_entries, 
from table_name tn1 join table_name tn2 on 
    tn1.year = tn2.year 
    and tn1.sid = tn2.sid 
    and tn1.user_id = tn2.user_id 
    and tn1.cid = tn2.cid
    and tn1.id <> tn2.id
group by tn1.id
order by tn1.id asc)
select ds.id from dupe_set ds where not exists 
 (select de from unnest(ds.duplicate_entries) as de where de < ds.id)

เลือก ID หมายเลขต่ำสุดที่มีซ้ำกัน (สมมติว่า ID กำลังเพิ่ม int PK) เหล่านี้จะเป็นรหัสที่คุณจะเก็บไว้รอบ ๆ


3

เพื่อให้ง่ายขึ้นฉันคิดว่าคุณต้องการใช้ข้อ จำกัด ที่ไม่ซ้ำกับคอลัมน์ปีและคีย์หลักคือคอลัมน์ชื่อ id

เพื่อหาค่าที่ซ้ำกันที่คุณควรเรียกใช้

SELECT year, COUNT(id)
FROM YOUR_TABLE
GROUP BY year
HAVING COUNT(id) > 1
ORDER BY COUNT(id);

เมื่อใช้คำสั่ง sql ด้านบนคุณจะได้รับตารางที่มีจำนวนปีที่ซ้ำกันทั้งหมดในตารางของคุณ เพื่อที่จะลบรายการที่ซ้ำทั้งหมดยกเว้นรายการที่ซ้ำกันล่าสุดคุณควรใช้คำสั่ง sql ข้างต้น

DELETE
FROM YOUR_TABLE A USING YOUR_TABLE_AGAIN B
WHERE A.year=B.year AND A.id<B.id;
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.