จะลบรายการที่ซ้ำกันได้อย่างไร?


92

ฉันต้องเพิ่มข้อ จำกัด เฉพาะให้กับตารางที่มีอยู่ ใช้ได้ดียกเว้นว่าตารางมีแถวเป็นล้านแถวแล้วและหลายแถวละเมิดข้อ จำกัด เฉพาะที่ฉันต้องเพิ่ม

วิธีที่เร็วที่สุดในการลบแถวที่กระทำผิดคืออะไร ฉันมีคำสั่ง SQL ซึ่งค้นหารายการที่ซ้ำกันและลบออก แต่ต้องใช้เวลาตลอดไปในการรัน มีวิธีอื่นในการแก้ปัญหานี้หรือไม่? อาจจะสำรองตารางแล้วกู้คืนหลังจากเพิ่มข้อ จำกัด ?

คำตอบ:


101

ตัวอย่างเช่นคุณสามารถ:

CREATE TABLE tmp ...
INSERT INTO tmp SELECT DISTINCT * FROM t;
DROP TABLE t;
ALTER TABLE tmp RENAME TO t;

2
คุณสามารถทำให้แตกต่างกันสำหรับกลุ่มคอลัมน์ อาจจะเป็น "SELECT DISTINCT (ta, tb, tc), * FROM t"?
gjrwebber


36
พิมพ์ง่ายกว่า: CREATE TABLE tmp AS SELECT ...;. จากนั้นคุณไม่จำเป็นต้องคิดเลยว่าเค้าโครงtmpคืออะไร :)
Randal Schwartz

9
คำตอบนี้ไม่ดีนักเนื่องจากสาเหตุหลายประการ @Randal ชื่อหนึ่ง. ในกรณีส่วนใหญ่โดยเฉพาะอย่างยิ่งถ้าคุณมีวัตถุขึ้นเช่นดัชนี จำกัด , วิว ฯลฯ วิธีการที่ดีกว่าคือการใช้จริงตารางชั่วคราว , ตัดต้นฉบับและอีกแทรกข้อมูล
Erwin Brandstetter

7
คุณพูดถูกเกี่ยวกับดัชนี การทิ้งและสร้างใหม่เร็วกว่ามาก แต่วัตถุอื่น ๆ ที่ขึ้นอยู่จะแตกหรือป้องกันไม่ให้วางตารางทั้งหมดซึ่ง OP จะค้นพบหลังจากที่ทำสำเนา - มากสำหรับ ถึงกระนั้นคุณก็พูดถูกเกี่ยวกับการโหวตลด มันไม่มีมูลเพราะมันไม่ใช่คำตอบที่ไม่ดี มันไม่ดีแค่นั้น คุณสามารถเพิ่มคำแนะนำบางอย่างเกี่ยวกับดัชนีหรือวัตถุขึ้นอยู่กับหรือลิงก์ไปยังคู่มือเช่นเดียวกับที่คุณทำในความคิดเห็นหรือคำอธิบายใด ๆ ฉันเดาว่าฉันรู้สึกหงุดหงิดเกี่ยวกับวิธีการโหวตของผู้คน ลบการโหวตลงคะแนน
Erwin Brandstetter

173

วิธีการเหล่านี้บางส่วนดูเหมือนซับซ้อนเล็กน้อยและโดยทั่วไปฉันจะทำสิ่งนี้เป็น:

ตารางที่กำหนดtableต้องการไม่ซ้ำกันบน (field1, field2) โดยรักษาแถวด้วย max field3:

DELETE FROM table USING table alias 
  WHERE table.field1 = alias.field1 AND table.field2 = alias.field2 AND
    table.max_field < alias.max_field

ตัวอย่างเช่นฉันมีตารางuser_accountsและฉันต้องการเพิ่มข้อ จำกัด เฉพาะในอีเมล แต่ฉันมีรายการที่ซ้ำกัน บอกด้วยว่าฉันต้องการเก็บรหัสที่สร้างล่าสุดไว้ (รหัสสูงสุดในรายการที่ซ้ำกัน)

DELETE FROM user_accounts USING user_accounts ua2
  WHERE user_accounts.email = ua2.email AND user_account.id < ua2.id;
  • หมายเหตุ - USINGไม่ใช่ SQL มาตรฐานเป็นส่วนขยาย PostgreSQL (แต่มีประโยชน์มาก) แต่คำถามเดิมกล่าวถึง PostgreSQL โดยเฉพาะ

4
แนวทางที่สองนั้นรวดเร็วมากใน postgres! ขอบคุณ.
Eric Bowman - abstracto -

5
@ ทิมคุณช่วยอธิบายได้ดีกว่าว่าUSINGทำอะไรใน postgresql?
Fopa Léon Constantin

3
นี่เป็นคำตอบที่ดีที่สุด แม้ว่าคุณจะไม่มีคอลัมน์อนุกรมในตารางเพื่อใช้สำหรับการเปรียบเทียบ ID แต่ก็คุ้มค่าที่จะเพิ่มคอลัมน์ชั่วคราวเพื่อใช้แนวทางง่ายๆนี้
Shane

2
ฉันเพิ่งตรวจสอบ คำตอบคือใช่มันจะ การใช้น้อยกว่า (<) จะทำให้คุณมีเพียงรหัสสูงสุดในขณะที่มากกว่า (>) ทำให้คุณเหลือเพียง id ขั้นต่ำโดยจะลบส่วนที่เหลือ
André C. Andersen

1
@ เชนหนึ่งสามารถใช้: WHERE table1.ctid<table2.ctid- ไม่จำเป็นต้องเพิ่มคอลัมน์อนุกรม
alexkovelsky

25

แทนที่จะสร้างตารางใหม่คุณยังสามารถแทรกแถวที่ไม่ซ้ำกันลงในตารางเดียวกันอีกครั้งได้หลังจากตัดทอนแล้ว ทำทั้งหมดในธุรกรรมเดียว

วิธีนี้มีประโยชน์เฉพาะเมื่อมีแถวจำนวนมากให้ลบจากทั่วทั้งตาราง DELETEเพียงไม่กี่รายการที่ซ้ำกันใช้ธรรมดา

คุณพูดถึงหลายล้านแถว เพื่อให้การดำเนินการรวดเร็วคุณต้องจัดสรรบัฟเฟอร์ชั่วคราวให้เพียงพอสำหรับเซสชัน ต้องปรับการตั้งค่าก่อนที่จะใช้บัฟเฟอร์ชั่วคราวในเซสชันปัจจุบันของคุณ ค้นหาขนาดโต๊ะของคุณ:

SELECT pg_size_pretty(pg_relation_size('tbl'));

ตั้งค่าtemp_buffersอย่างน้อยกว่านั้นเล็กน้อย

SET temp_buffers = 200MB;   -- example value

BEGIN;

CREATE TEMP TABLE t_tmp AS  -- retains temp for duration of session
SELECT DISTINCT * FROM tbl  -- DISTINCT folds duplicates
ORDER  BY id;               -- optionally "cluster" data

TRUNCATE tbl;

INSERT INTO tbl
SELECT * FROM t_tmp;        -- retains order (implementation detail)

COMMIT;

วิธีนี้ดีกว่าการสร้างตารางใหม่หากมีวัตถุขึ้นอยู่ มุมมองดัชนีคีย์ต่างประเทศหรือวัตถุอื่น ๆ ที่อ้างถึงตาราง TRUNCATEทำให้คุณเริ่มต้นด้วยกระดานชนวนสะอาดอยู่แล้ว (ไฟล์ใหม่ในพื้นหลัง) และเป็นมากเร็วกว่าDELETE FROM tblด้วยโต๊ะขนาดใหญ่ ( DELETEสามารถจริงจะได้เร็วขึ้นด้วยตารางเล็ก)

สำหรับตารางขนาดใหญ่การดร็อปดัชนีและคีย์ต่างประเทศ (FK) จะเร็วกว่าเป็นประจำเติมตารางและสร้างวัตถุเหล่านี้ใหม่ เท่าที่ข้อ จำกัด FK เกี่ยวข้องคุณต้องมั่นใจว่าข้อมูลใหม่นั้นถูกต้องแน่นอนมิฉะนั้นคุณจะพบข้อยกเว้นในการพยายามสร้าง FK

โปรดทราบว่าต้องล็อคก้าวร้าวมากขึ้นกว่าTRUNCATE DELETEนี่อาจเป็นปัญหาสำหรับตารางที่มีการโหลดพร้อมกันจำนวนมาก แต่ก็ยังก่อกวนน้อยกว่าการวางและแทนที่โต๊ะอย่างสมบูรณ์

หากTRUNCATEไม่ใช่ตัวเลือกหรือโดยทั่วไปสำหรับตารางขนาดเล็กถึงขนาดกลางมีเทคนิคที่คล้ายกันกับCTE ที่ปรับเปลี่ยนข้อมูล (Postgres 9.1 +):

WITH del AS (DELETE FROM tbl RETURNING *)
INSERT INTO tbl
SELECT DISTINCT * FROM del;
ORDER  BY id; -- optionally "cluster" data while being at it.

ช้ากว่าสำหรับโต๊ะใหญ่เพราะTRUNCATEเร็วกว่า แต่อาจเร็วกว่า (และง่ายกว่า!) สำหรับโต๊ะขนาดเล็ก

หากคุณไม่มีวัตถุขึ้นอยู่เลยคุณอาจสร้างตารางใหม่และลบตารางเก่า แต่คุณแทบจะไม่ได้ประโยชน์อะไรเลยจากแนวทางสากล

สำหรับตารางขนาดใหญ่ที่ไม่พอดีกับRAM ที่มีอยู่การสร้างตารางใหม่จะเร็วกว่ามาก คุณจะต้องชั่งน้ำหนักกับปัญหา / ค่าใช้จ่ายที่อาจเกิดขึ้นกับวัตถุ


2
ฉันก็ใช้แนวทางนี้เหมือนกัน อย่างไรก็ตามมันอาจเป็นแบบส่วนตัว แต่ตารางชั่วคราวของฉันถูกลบและไม่สามารถใช้งานได้หลังจากการตัดทอน ... โปรดใช้ความระมัดระวังในการทำขั้นตอนเหล่านั้นหากสร้างตารางชั่วคราวสำเร็จและพร้อมใช้งาน
xlash

@xlash: คุณสามารถตรวจสอบการมีอยู่เพื่อให้แน่ใจและใช้ชื่ออื่นสำหรับตารางชั่วคราวหรือใช้ชื่อที่มีอยู่ซ้ำก็ได้ .. ฉันเพิ่มคำตอบเล็กน้อย
Erwin Brandstetter

คำเตือน: ระวัง +1 เพื่อ @xlash - TRUNCATEฉันมีอีกครั้งนำเข้าข้อมูลของฉันเพราะตารางชั่วคราวหลังจากที่ไม่มีอยู่จริง ดังที่เออร์วินกล่าวว่าอย่าลืมตรวจสอบให้แน่ใจก่อนที่จะตัดทอนตารางของคุณ ดูคำตอบของ @ codebykat
Jordan Arseno

1
@JordanArseno: ฉันเปลี่ยนไปใช้เวอร์ชันที่ไม่มีON COMMIT DROPเพื่อให้คนที่พลาดส่วนที่ฉันเขียนว่า "ในรายการเดียว" จะไม่สูญเสียข้อมูล และฉันได้เพิ่ม BEGIN / COMMIT เพื่อชี้แจง "รายการเดียว"
Erwin Brandstetter

1
การแก้ปัญหาด้วย USING ใช้เวลานานกว่า 3 ชั่วโมงบนโต๊ะโดยมีข้อมูล 14 ล้านรายการ การแก้ปัญหาด้วย temp_buffers นี้ใช้เวลา 13 นาที ขอบคุณ.
โยน

20

คุณสามารถใช้ oid หรือ ctid ซึ่งโดยปกติเป็นคอลัมน์ที่ "มองไม่เห็น" ในตาราง:

DELETE FROM table
 WHERE ctid NOT IN
  (SELECT MAX(s.ctid)
    FROM table s
    GROUP BY s.column_has_be_distinct);

4
สำหรับการลบในสถานที่ , NOT EXISTSควรจะเร็วมากขึ้น : DELETE FROM tbl t WHERE EXISTS (SELECT 1 FROM tbl t1 WHERE t1.dist_col = t.dist_col AND t1.ctid > t.ctid)- หรือใช้คอลัมน์อื่น ๆ หรือชุดของคอลัมน์สำหรับการเรียงลำดับการที่จะเลือกผู้รอดชีวิต
Erwin Brandstetter

@ErwinBrandstetter แบบสอบถามที่คุณระบุควรจะใช้NOT EXISTSหรือไม่?
John

1
@ จอห์น: มันต้องอยู่EXISTSที่นี่ อ่านดังนี้: "ลบแถวทั้งหมดที่มีแถวอื่นที่มีค่าเท่ากันdist_colแต่ใหญ่กว่าctid" ctidคนเดียวที่รอดต่อกลุ่มของหลอกจะเป็นหนึ่งเดียวกับที่ใหญ่ที่สุด
Erwin Brandstetter

ทางออกที่ง่ายที่สุดหากคุณมีแถวที่ซ้ำกันเพียงไม่กี่แถว สามารถใช้กับLIMITถ้าคุณทราบจำนวนที่ซ้ำกัน
Skippy le Grand Gourou

19

ฟังก์ชันหน้าต่าง PostgreSQL มีประโยชน์สำหรับปัญหานี้

DELETE FROM tablename
WHERE id IN (SELECT id
              FROM (SELECT id,
                             row_number() over (partition BY column1, column2, column3 ORDER BY id) AS rnum
                     FROM tablename) t
              WHERE t.rnum > 1);

ดูรายการที่ซ้ำกันลบ


และการใช้ "ctid" แทน "id" วิธีนี้ใช้ได้กับแถวที่ซ้ำกันทั้งหมด
bradw2k

ทางออกที่ดี ฉันต้องทำสิ่งนี้สำหรับตารางที่มีบันทึกเป็นพันล้านรายการ ฉันเพิ่ม WHERE ใน SELECT ด้านในเพื่อทำมันเป็นชิ้น ๆ
ม.ค.

8

ข้อความค้นหาทั่วไปเพื่อลบรายการที่ซ้ำกัน:

DELETE FROM table_name
WHERE ctid NOT IN (
  SELECT max(ctid) FROM table_name
  GROUP BY column1, [column 2, ...]
);

คอลัมน์ctidนี้เป็นคอลัมน์พิเศษสำหรับทุกตาราง แต่ไม่สามารถมองเห็นได้เว้นแต่จะระบุไว้เป็นพิเศษ ctidค่าคอลัมน์ถือว่าไม่ซ้ำกันสำหรับแถวในตารางทุก ดูPostgreSQL คอลัมน์ระบบctidเพื่อเรียนรู้เพิ่มเติมเกี่ยวกับ


1
คำตอบสากลเดียว! ทำงานได้โดยไม่ต้องเข้าร่วมด้วยตนเอง / คาร์ทีเซียน ควรเพิ่มแม้ว่าการระบุGROUP BYอนุประโยคให้ถูกต้องเป็นสิ่งสำคัญซึ่งควรเป็น "เกณฑ์ความเป็นเอกลักษณ์" ที่ละเมิดในขณะนี้หรือหากคุณต้องการให้คีย์ตรวจหารายการที่ซ้ำกัน หากระบุผิดมันจะทำงานไม่ถูกต้อง
msciwoj

7

จากรายชื่อผู้รับจดหมาย postgresql.org เก่า :

create table test ( a text, b text );

ค่าที่ไม่ซ้ำกัน

insert into test values ( 'x', 'y');
insert into test values ( 'x', 'x');
insert into test values ( 'y', 'y' );
insert into test values ( 'y', 'x' );

ค่าที่ซ้ำกัน

insert into test values ( 'x', 'y');
insert into test values ( 'x', 'x');
insert into test values ( 'y', 'y' );
insert into test values ( 'y', 'x' );

ซ้ำอีกสองรายการ

insert into test values ( 'x', 'y');

select oid, a, b from test;

เลือกแถวที่ซ้ำกัน

select o.oid, o.a, o.b from test o
    where exists ( select 'x'
                   from test i
                   where     i.a = o.a
                         and i.b = o.b
                         and i.oid < o.oid
                 );

ลบแถวที่ซ้ำกัน

หมายเหตุ: PostgreSQL ไม่รองรับนามแฝงในตารางที่กล่าวถึงในfromประโยคการลบ

delete from test
    where exists ( select 'x'
                   from test i
                   where     i.a = test.a
                         and i.b = test.b
                         and i.oid < test.oid
             );

คำอธิบายของคุณฉลาดมาก แต่คุณพลาดจุดหนึ่งในสร้างตารางระบุ oid จากนั้นเข้าถึงเฉพาะการแสดงข้อความแสดงข้อผิดพลาด oid else
Kalanidhi

@Kalanidhi ขอบคุณสำหรับความคิดเห็นของคุณเกี่ยวกับการปรับปรุงคำตอบฉันจะพิจารณาประเด็นนี้
Bhavik Ambani

อันนี้มาจากpostgresql.org/message-id/…
Martin F

คุณสามารถใช้คอลัมน์ระบบ 'ctid' ได้หาก 'oid' แสดงข้อผิดพลาด
sul4bh

4

ฉันเพิ่งใช้คำตอบของ Erwin Brandstetterเพื่อลบรายการที่ซ้ำกันในตารางเข้าร่วมได้สำเร็จ (ตารางที่ไม่มีรหัสหลักของตัวเอง) แต่พบว่ามีข้อแม้ที่สำคัญอย่างหนึ่ง

การรวมON COMMIT DROPหมายถึงตารางชั่วคราวจะหลุดเมื่อสิ้นสุดธุรกรรม สำหรับฉันนั่นหมายความว่าตารางชั่วคราวไม่สามารถใช้งานได้อีกต่อไปเมื่อฉันเข้าไปแทรก!

ฉันเพิ่งทำCREATE TEMPORARY TABLE t_tmp AS SELECT DISTINCT * FROM tbl;และทุกอย่างทำงานได้ดี

ตารางชั่วคราวจะหลุดเมื่อสิ้นสุดเซสชัน


3

ฟังก์ชันนี้จะลบรายการที่ซ้ำกันโดยไม่ต้องลบดัชนีและทำกับตารางใด ๆ

การใช้งาน: select remove_duplicates('mytable');

---
--- remove_duplicates (tablename) ลบระเบียนที่ซ้ำกันออกจากตาราง (แปลงจาก set เป็น set ที่ไม่ซ้ำกัน)
---
สร้างหรือแทนที่ฟังก์ชัน remove_duplicates (ข้อความ) RETURNS เป็นโมฆะเป็น $$
ประกาศ
  ชื่อตาราง ALIAS ในราคา $ 1;
เริ่ม
  ดำเนินการ 'สร้างตารางชั่วคราว _DISTINCT_' || ชื่อตาราง || 'AS (เลือก DISTINCT * จาก' || ชื่อตาราง || ');';
  ดำเนินการ 'ลบออกจาก' || ชื่อตาราง || ';';
  ดำเนินการ 'INSERT INTO' || ชื่อตาราง || '(เลือก * จาก _DISTINCT_' || ชื่อตาราง || ');';
  ดำเนินการ 'วางตาราง _DISTINCT_' || ชื่อตาราง || ';';
  กลับ;
จบ;
$$ LANGUAGE plpgsql;

3
DELETE FROM table
  WHERE something NOT IN
    (SELECT     MAX(s.something)
      FROM      table As s
      GROUP BY  s.this_thing, s.that_thing);

นั่นคือสิ่งที่ฉันกำลังทำอยู่ แต่ใช้เวลานานมากในการเรียกใช้
gjrwebber

1
สิ่งนี้จะไม่ล้มเหลวหากหลายแถวในตารางมีค่าเดียวกันในคอลัมน์หรือไม่?
shreedhar

3

หากคุณมีรายการที่ซ้ำกันเพียงรายการเดียวหรือสองสามรายการและรายการเหล่านั้นซ้ำกัน (นั่นคือปรากฏสองครั้ง) คุณสามารถใช้ctidคอลัมน์"ซ่อน" ตามที่เสนอข้างต้นร่วมกับLIMIT:

DELETE FROM mytable WHERE ctid=(SELECT ctid FROM mytable WHERE […] LIMIT 1);

การดำเนินการนี้จะลบเฉพาะแถวแรกของแถวที่เลือก


ฉันรู้ว่ามันไม่ได้ช่วยแก้ปัญหาของ OP ซึ่งมีข้อมูลซ้ำกันมากในหลายล้านแถว แต่มันก็อาจจะมีประโยชน์อยู่ดี
Skippy le Grand Gourou

สิ่งนี้จะต้องถูกเรียกใช้หนึ่งครั้งสำหรับแต่ละแถวที่ซ้ำกัน คำตอบของ shekwi ต้องเรียกใช้เพียงครั้งเดียว
bradw2k

3

ขั้นแรกคุณต้องตัดสินใจว่าคุณจะเก็บ "รายการที่ซ้ำกัน" รายการใดไว้ หากคอลัมน์ทั้งหมดเท่ากันคุณสามารถลบคอลัมน์ใดก็ได้ ... แต่บางทีคุณอาจต้องการเก็บเฉพาะข้อมูลล่าสุดหรือเกณฑ์อื่น ๆ

วิธีที่เร็วที่สุดขึ้นอยู่กับคำตอบของคุณสำหรับคำถามข้างต้นและ% ของรายการที่ซ้ำกันในตาราง หากคุณทิ้งแถวไป 50% คุณจะทำได้ดีกว่าCREATE TABLE ... AS SELECT DISTINCT ... FROM ... ;และถ้าคุณลบ 1% ของแถวการใช้ DELETE จะดีกว่า

นอกจากนี้สำหรับการบำรุงรักษาเช่นนี้โดยทั่วไปแล้วคุณควรตั้งค่าwork_memRAM เป็นส่วนที่ดี: เรียกใช้ EXPLAIN ตรวจสอบจำนวนประเภท / แฮช N และตั้งค่า work_mem เป็น RAM / 2 / N ของคุณใช้ RAM จำนวนมาก มันดีสำหรับความเร็ว ตราบใดที่คุณมีการเชื่อมต่อพร้อมกันเพียงครั้งเดียว ...


1

ฉันกำลังทำงานกับ PostgreSQL 8.4 เมื่อฉันรันโค้ดที่เสนอฉันพบว่าไม่ได้ลบรายการที่ซ้ำกันออกไป ในการเรียกใช้การทดสอบบางอย่างฉันพบว่าการเพิ่ม "DISTINCT ON (duplicate_column_name)" และ "ORDER BY duplicate_column_name" เป็นเคล็ดลับ ฉันไม่ใช่ผู้เชี่ยวชาญด้าน SQL ฉันพบสิ่งนี้ในเอกสาร PostgreSQL 8.4 SELECT ... DISTINCT

CREATE OR REPLACE FUNCTION remove_duplicates(text, text) RETURNS void AS $$
DECLARE
  tablename ALIAS FOR $1;
  duplicate_column ALIAS FOR $2;
BEGIN
  EXECUTE 'CREATE TEMPORARY TABLE _DISTINCT_' || tablename || ' AS (SELECT DISTINCT ON (' || duplicate_column || ') * FROM ' || tablename || ' ORDER BY ' || duplicate_column || ' ASC);';
  EXECUTE 'DELETE FROM ' || tablename || ';';
  EXECUTE 'INSERT INTO ' || tablename || ' (SELECT * FROM _DISTINCT_' || tablename || ');';
  EXECUTE 'DROP TABLE _DISTINCT_' || tablename || ';';
  RETURN;
END;
$$ LANGUAGE plpgsql;


1
DELETE FROM tablename
WHERE id IN (SELECT id
    FROM (SELECT id,ROW_NUMBER() OVER (partition BY column1, column2, column3 ORDER BY id) AS rnum
                 FROM tablename) t
          WHERE t.rnum > 1);

ลบรายการที่ซ้ำกันตามคอลัมน์และเก็บแถวที่มีรหัสต่ำสุด รูปแบบนี้นำมาจากไฟล์วิกิ postgres

การใช้ CTE คุณสามารถบรรลุเวอร์ชันที่อ่านได้ง่ายขึ้นจากข้างต้น

WITH duplicate_ids as (
    SELECT id, rnum 
    FROM num_of_rows
    WHERE rnum > 1
),
num_of_rows as (
    SELECT id, 
        ROW_NUMBER() over (partition BY column1, 
                                        column2, 
                                        column3 ORDER BY id) AS rnum
        FROM tablename
)
DELETE FROM tablename 
WHERE id IN (SELECT id from duplicate_ids)

1
CREATE TABLE test (col text);
INSERT INTO test VALUES
 ('1'),
 ('2'), ('2'),
 ('3'),
 ('4'), ('4'),
 ('5'),
 ('6'), ('6');
DELETE FROM test
 WHERE ctid in (
   SELECT t.ctid FROM (
     SELECT row_number() over (
               partition BY col
               ORDER BY col
               ) AS rnum,
            ctid FROM test
       ORDER BY col
     ) t
    WHERE t.rnum >1);

ฉันทดสอบแล้วและได้ผล ฉันจัดรูปแบบให้อ่านง่าย มันดูซับซ้อนมาก แต่ก็สามารถใช้คำอธิบายได้ จะเปลี่ยนตัวอย่างนี้อย่างไรสำหรับกรณีการใช้งานของตัวเอง?
Tobias
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.