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


1285

วิธีที่ดีที่สุดในการลบแถวที่ซ้ำกันออกจากSQL Serverตารางมีขนาดค่อนข้างใหญ่(เช่น 300,000+ แถว) คืออะไร

แน่นอนว่าแถวจะไม่ซ้ำกันอย่างสมบูรณ์เพราะมีอยู่ของRowIDเขตข้อมูลตัวตน

MyTable

RowID int not null identity(1,1) primary key,
Col1 varchar(20) not null,
Col2 varchar(2048) not null,
Col3 tinyint not null

13
เคล็ดลับฉบับย่อสำหรับผู้ใช้ PostgreSQL ที่อ่านสิ่งนี้ (จำนวนมากจะเชื่อมโยงกับความถี่): Pg ไม่เปิดเผยคำศัพท์ CTE ในมุมมองที่สามารถอัปเดตได้ดังนั้นคุณจึงไม่สามารถDELETE FROMเป็นคำศัพท์ CTE ได้โดยตรง ดูstackoverflow.com/q/18439054/398670
Craig Ringer

@CraigRinger เหมือนกันจริงสำหรับSybase - ฉันได้รวบรวมโซลูชั่นที่เหลืออยู่ที่นี่ (ควรใช้ได้กับ PG และอื่น ๆ ด้วยเช่นกัน: stackoverflow.com/q/19544489/1855801 (แทนที่ROWID()ฟังก์ชันด้วยคอลัมน์ RowID ถ้ามี)
maf-soft

12
เพียงเพิ่มคำเตือนที่นี่ เมื่อรันกระบวนการยกเลิกการทำซ้ำให้ตรวจสอบสิ่งที่คุณลบก่อนอีกครั้งเสมอ! นี่เป็นหนึ่งในพื้นที่ที่พบเห็นได้บ่อยมากในการลบข้อมูลที่ดี
Jeff Davis

คำตอบ:


1142

สมมติว่าไม่มี nulls คุณGROUP BYคอลัมน์ที่ไม่ซ้ำกันและRowId เป็นแถวเพื่อให้ จากนั้นเพียงลบทุกอย่างที่ไม่มีรหัสแถว:SELECTMIN (or MAX)

DELETE FROM MyTable
LEFT OUTER JOIN (
   SELECT MIN(RowId) as RowId, Col1, Col2, Col3 
   FROM MyTable 
   GROUP BY Col1, Col2, Col3
) as KeepRows ON
   MyTable.RowId = KeepRows.RowId
WHERE
   KeepRows.RowId IS NULL

ในกรณีที่คุณมี GUID แทนที่จะเป็นจำนวนเต็มคุณสามารถแทนที่

MIN(RowId)

กับ

CONVERT(uniqueidentifier, MIN(CONVERT(char(36), MyGuidColumn)))

327
จะใช้งานได้เช่นกัน? DELETE FROM MyTable WHERE RowId NOT IN (SELECT MIN(RowId) FROM MyTable GROUP BY Col1, Col2, Col3);
Georg Schölly

10
@Andriy - ใน SQL Server LEFT JOINที่มีประสิทธิภาพน้อยกว่าNOT EXISTS sqlinthewild.co.za/index.php/2010/03/23/...เว็บไซต์เดียวกันยังเปรียบเทียบVSNOT IN sqlinthewild.co.za/index.php/2010/02/18/not-exists-vs-not-in Out of 3 ฉันคิดว่าทำได้ดีที่สุด ทั้งสามจะสร้างแผนด้วยการเข้าร่วมด้วยตนเองแม้ว่าจะสามารถหลีกเลี่ยงได้ NOT EXISTSNOT EXISTS
Martin Smith

12
@ Martin, @Gorg: ดังนั้นฉันได้ทำการทดสอบเล็กน้อย ตารางขนาดใหญ่ถูกสร้างขึ้นและเติมข้อมูลตามที่อธิบายไว้ที่นี่: sqlinthewild.co.za/index.php/2010/03/23/…มีการเลือกสองรายการแล้วหนึ่งรายการโดยใช้ LEFT JOIN + WHERE IS NULL เทคนิคอื่น ๆ โดยใช้ NOT ในหนึ่งเดียว จากนั้นฉันก็ดำเนินการตามแผนปฏิบัติการและคาดเดาอะไร ค่าใช้จ่ายในการสืบค้นเท่ากับ 18% สำหรับ LEFT JOIN เทียบกับ 82% สำหรับ NOT IN ซึ่งเป็นเรื่องประหลาดใจสำหรับฉัน ฉันอาจทำสิ่งที่ฉันไม่ควรมีหรือกลับกันซึ่งถ้าเป็นจริงฉันก็อยากจะรู้
Andriy M

16
@ GeorgSchöllyให้คำตอบที่สง่างาม ฉันใช้มันในตารางที่ข้อผิดพลาด PHP ของฉันสร้างแถวที่ซ้ำกัน
ฟิลิป Kearns

12
ขออภัย แต่ทำไมDELETE MyTable FROM MyTableไวยากรณ์ที่ถูกต้อง? ฉันไม่เห็นใส่ชื่อตารางขวาหลังจากDELETEเป็นตัวเลือกในเอกสารประกอบที่นี่ ขออภัยหากคนอื่นเห็นได้ชัด ฉันเป็นมือใหม่กับ SQL เพียงแค่พยายามเรียนรู้ สำคัญกว่าสาเหตุที่ใช้งาน: อะไรคือความแตกต่างระหว่างการรวมชื่อของตารางที่มีหรือไม่
levininja

760

อีกวิธีที่เป็นไปได้ในการทำเช่นนี้คือ

; 

--Ensure that any immediately preceding statement is terminated with a semicolon above
WITH cte
     AS (SELECT ROW_NUMBER() OVER (PARTITION BY Col1, Col2, Col3 
                                       ORDER BY ( SELECT 0)) RN
         FROM   #MyTable)
DELETE FROM cte
WHERE  RN > 1;

ฉันกำลังใช้ORDER BY (SELECT 0)ด้านบนเนื่องจากเป็นกฎเกณฑ์ที่จะรักษาแถวในกรณีที่มีการเสมอกัน

เพื่อรักษาล่าสุดRowIDเช่นคุณสามารถใช้ORDER BY RowID DESC

แผนการดำเนินการ

แผนการดำเนินการสำหรับเรื่องนี้มักจะง่ายและมีประสิทธิภาพมากกว่าคำตอบที่ยอมรับเพราะไม่จำเป็นต้องเข้าร่วมด้วยตนเอง

แผนการดำเนินการ

นี่ไม่ใช่กรณีเสมอไป ที่แห่งหนึ่งซึ่งGROUP BYอาจเป็นที่ต้องการของการแก้ปัญหาคือสถานการณ์ที่มีการเลือกแฮชรวมเพื่อเลือกกระแสรวม

การROW_NUMBERแก้ปัญหาจะให้แผนเดียวกันในขณะที่GROUP BYกลยุทธ์มีความยืดหยุ่นมากกว่า

แผนการดำเนินการ

ปัจจัยที่อาจสนับสนุนวิธีแฮชรวมจะเป็น

  • ไม่มีดัชนีที่เป็นประโยชน์ในคอลัมน์การแบ่งพาร์ติชัน
  • ค่อนข้างน้อยกว่ากลุ่มที่ค่อนข้างซ้ำซ้อนในแต่ละกลุ่ม

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


28
ถ้าฉันอาจเพิ่ม: uniqueidentifierคำตอบที่ได้รับการยอมรับไม่ได้ทำงานกับตารางที่ใช้ อันนี้ง่ายกว่ามากและใช้งานได้ดีบนโต๊ะใด ๆ ขอบคุณมาร์ติน
BrunoLM

15
นี่เป็นคำตอบที่ยอดเยี่ยมมาก! มันใช้งานได้เมื่อฉันลบ PK เก่าออกก่อนที่ฉันจะรู้ว่ามีที่ที่ซ้ำกัน +100
มิคาเอลอีเลียสสัน

12
ฉันขอแนะนำให้ถามแล้วตอบคำถามนี้ (พร้อมคำตอบนี้) ใน DBA.SE จากนั้นเราสามารถเพิ่มลงในรายการคำตอบที่ยอมรับได้ของเรา
Nick Chammas

16
แตกต่างจากคำตอบที่ยอมรับนี้ยังทำงานในตารางที่ไม่มีคีย์ ( RowId) เพื่อเปรียบเทียบ
vossad01

8
หนึ่งนี้ไม่ได้ทำงานในทุกรุ่นเซิร์ฟเวอร์ SQL บนมืออื่น ๆ
เดวิด

150

มีบทความที่ดีเกี่ยวกับการลบรายการที่ซ้ำกันในเว็บไซต์ Microsoft Support มันค่อนข้างอนุรักษ์นิยม - พวกเขาให้คุณทำทุกอย่างแยกขั้นตอน - แต่มันควรจะทำงานได้ดีกับโต๊ะขนาดใหญ่

ฉันเคยใช้การรวมตัวเองเพื่อทำสิ่งนี้ในอดีตแม้ว่ามันอาจจะน่ารักขึ้นมาพร้อมกับส่วนคำสั่ง HAVING:

DELETE dupes
FROM MyTable dupes, MyTable fullTable
WHERE dupes.dupField = fullTable.dupField 
AND dupes.secondDupField = fullTable.secondDupField 
AND dupes.uniqueField > fullTable.uniqueField

ที่สมบูรณ์แบบ! ฉันพบว่านี่เป็นวิธีที่มีประสิทธิภาพที่สุดในการลบแถวที่ซ้ำกันใน mariadb รุ่นเก่าของฉัน 10.1.xx ขอบคุณ!
เมาม

ง่ายกว่าและเข้าใจง่ายกว่ามาก!
Marc

98

แบบสอบถามต่อไปนี้มีประโยชน์ในการลบแถวที่ซ้ำกัน ตารางในตัวอย่างนี้มีIDเป็นคอลัมน์ประจำตัวและคอลัมน์ที่มีข้อมูลที่ซ้ำกันColumn1, และColumn2Column3

DELETE FROM TableName
WHERE  ID NOT IN (SELECT MAX(ID)
                  FROM   TableName
                  GROUP  BY Column1,
                            Column2,
                            Column3
                  /*Even if ID is not null-able SQL Server treats MAX(ID) as potentially
                    nullable. Because of semantics of NOT IN (NULL) including the clause
                    below can simplify the plan*/
                  HAVING MAX(ID) IS NOT NULL) 

การใช้สคริปต์ต่อไปนี้แสดงของGROUP BY, HAVING, ORDER BYในแบบสอบถามหนึ่งและผลตอบแทนที่มีคอลัมน์ที่ซ้ำกันและนับ

SELECT YourColumnName,
       COUNT(*) TotalCount
FROM   YourTableName
GROUP  BY YourColumnName
HAVING COUNT(*) > 1
ORDER  BY COUNT(*) DESC 

1
ข้อผิดพลาด MySQL กับสคริปต์แรก 'คุณไม่สามารถระบุตารางเป้าหมาย' TableName 'สำหรับการปรับปรุงในข้อ FROM'
D.Rosado

นอกเหนือจากข้อผิดพลาด D.Rosado รายงานแล้วแบบสอบถามแรกของคุณยังช้ามาก เคียวรี SELECT ที่สอดคล้องกันใช้เวลาในการตั้งค่าของฉัน + - นานกว่าคำตอบที่ยอมรับ 20 เท่า
parvus

8
@parvus - คำถามถูกติดแท็ก SQL Server ไม่ใช่ MySQL ไวยากรณ์นั้นใช้ได้ใน SQL Server นอกจากนี้ยัง MySQL เป็นฉาวโฉ่ไม่ดีในการเพิ่มประสิทธิภาพคำสั่งย่อยดูตัวอย่างที่นี่ คำตอบนี้ใช้ได้ใน SQL Server ในความเป็นจริงมักจะมีประสิทธิภาพดีกว่าNOT IN OUTER JOIN ... NULLฉันจะเพิ่มลงHAVING MAX(ID) IS NOT NULLในแบบสอบถามแม้ว่าจะไม่จำเป็นต้องมีความหมายเพราะสามารถปรับปรุงตัวอย่าง
Martin Smith

2
ทำงานได้ดีใน PostgreSQL 8.4
ทิศเหนือ

63
delete t1
from table t1, table t2
where t1.columnA = t2.columnA
and t1.rowid>t2.rowid

Postgres:

delete
from table t1
using table t2
where t1.columnA = t2.columnA
and t1.rowid > t2.rowid

เหตุใดจึงโพสต์โซลูชัน Postgres ในคำถาม SQL Server
Lankymart

2
@Lankymart เนื่องจากมีผู้ใช้ postgres มาที่นี่ด้วย ดูคะแนนคำตอบนี้
Gabriel

2
ผมเคยเห็นในคำถามบาง SQL นิยมเช่นเดียวกับในที่นี่ , ที่นี่และที่นี่ OP ได้รับคำตอบของเขาและทุกคนก็ได้รับความช่วยเหลือเช่นกัน ไม่มีปัญหา IMHO
Gabriel

44
DELETE LU 
FROM   (SELECT *, 
               Row_number() 
                 OVER ( 
                   partition BY col1, col1, col3 
                   ORDER BY rowid DESC) [Row] 
        FROM   mytable) LU 
WHERE  [row] > 1 

1
ฉันได้รับข้อความนี้ใน SQL DW ของ Azure: ส่วนคำสั่ง FROM ไม่ได้รับการสนับสนุนในคำสั่ง DELETE
Amit

40

การดำเนินการนี้จะลบแถวที่ซ้ำกันยกเว้นแถวแรก

DELETE
FROM
    Mytable
WHERE
    RowID NOT IN (
        SELECT
            MIN(RowID)
        FROM
            Mytable
        GROUP BY
            Col1,
            Col2,
            Col3
    )

อ้างอิง ( http://www.codeproject.com/Articles/157977/Remove-Duplicate-Rows-from-a-Table-in-SQL-Server )


10
สำหรับ mysql จะให้ข้อผิดพลาด: รหัสข้อผิดพลาด: 1093 คุณไม่สามารถระบุตารางเป้าหมาย 'Mytable' สำหรับการปรับปรุงในอนุประโยค FROM แต่การเปลี่ยนแปลงเล็ก ๆ นี้จะทำงานสำหรับ mysql: ลบออกจาก Mytable ซึ่ง Rowid ไม่ได้อยู่ใน (เลือก ID จาก (เลือก MIN (RowID) เป็น ID จากกลุ่ม Mytable โดย Col1, Col2, Col3) เป็นอุณหภูมิ)
Ritesh

35

ฉันต้องการ CTE สำหรับการลบแถวที่ซ้ำกันออกจากตารางเซิร์ฟเวอร์ sql

ขอแนะนำให้ติดตามบทความนี้: http://codaffection.com/sql-server-article/delete-duplicate-rows-in-sql-server/

โดยการรักษาต้นฉบับ

WITH CTE AS
(
SELECT *,ROW_NUMBER() OVER (PARTITION BY col1,col2,col3 ORDER BY col1,col2,col3) AS RN
FROM MyTable
)

DELETE FROM CTE WHERE RN<>1

โดยไม่ต้องรักษาต้นฉบับ

WITH CTE AS
(SELECT *,R=RANK() OVER (ORDER BY col1,col2,col3)
FROM MyTable)
 
DELETE CTE
WHERE R IN (SELECT R FROM CTE GROUP BY R HAVING COUNT(*)>1)

24

วิธีดึงข้อมูลแถวซ้ำ:

SELECT
name, email, COUNT(*)
FROM 
users
GROUP BY
name, email
HAVING COUNT(*) > 1

วิธีลบแถวซ้ำ:

DELETE users 
WHERE rowid NOT IN 
(SELECT MIN(rowid)
FROM users
GROUP BY name, email);      

สำหรับผู้ใช้งาน MySQL โปรดทราบว่าก่อนอื่นต้องเป็นDELETE FROMอย่างที่สองมันไม่ทำงานเพราะคุณไม่สามารถSELECTอยู่ในตารางเดียวกับที่คุณเข้าDELETEมา ใน MySQL MySQL error 1093ลั่นออกนี้
ÍhorMé

23

รวดเร็วและสกปรกในการลบแถวที่ซ้ำกันแน่นอน (สำหรับตารางเล็ก ๆ ):

select  distinct * into t2 from t1;
delete from t1;
insert into t1 select *  from t2;
drop table t2;

3
โปรดทราบว่าคำถามนั้นระบุการทำซ้ำที่ไม่แน่นอน (id แถวคู่)
Dennis Jaheruddin

21

ฉันชอบ subquery \ having count (*)> 1 วิธีแก้ปัญหาในการรวมภายในเพราะฉันพบว่าอ่านง่ายขึ้นและมันง่ายมากที่จะเปลี่ยนเป็นคำสั่ง SELECT เพื่อตรวจสอบสิ่งที่จะถูกลบก่อนที่คุณจะเรียกใช้

--DELETE FROM table1 
--WHERE id IN ( 
     SELECT MIN(id) FROM table1 
     GROUP BY col1, col2, col3 
     -- could add a WHERE clause here to further filter
     HAVING count(*) > 1
--)

ไม่ลบระเบียนทั้งหมดที่ปรากฏในการสืบค้นภายใน เราจำเป็นต้องลบรายการที่ซ้ำกันและเก็บรักษาไว้ดั้งเดิม
Sandy

3
คุณส่งคืนโดยใช้ id ต่ำสุดโดยอิงจาก min (id) ในส่วนคำสั่งที่เลือก
James Errico

2
ไม่ใส่เครื่องหมายคอมเม้นท์บรรทัดแรก, วินาทีและสุดท้ายของเคียวรี
James Errico

7
สิ่งนี้จะไม่ล้างข้อมูลที่ซ้ำกันทั้งหมด หากคุณมี 3 แถวที่ซ้ำกันมันจะเลือกแถวที่มี MIN (id) และลบแถวนั้นทิ้งแถวที่เหลือสองแถวที่ซ้ำกัน
Chloe

2
อย่างไรก็ตามฉันลงเอยด้วยการใช้คำสั่งนี้ซ้ำไปซ้ำมาอีกครั้งเพื่อที่มันจะได้ดำเนินการจริงแทนที่จะมีการเชื่อมต่อหมดเวลาหรือคอมพิวเตอร์เข้าสู่โหมดสลีป ฉันเปลี่ยนเป็นMAX(id)ลบรายการที่ซ้ำหลังและเพิ่มลงLIMIT 1000000ในคิวรีด้านในดังนั้นจึงไม่ต้องสแกนทั้งตาราง สิ่งนี้แสดงความคืบหน้าได้เร็วกว่าคำตอบอื่น ๆ ซึ่งดูเหมือนจะแขวนนานหลายชั่วโมง หลังจากตารางถูกตัดให้มีขนาดที่จัดการได้แล้วคุณสามารถทำแบบสอบถามอื่นให้เสร็จได้ เคล็ดลับ: ตรวจสอบว่า col1 / col2 / col3 มีดัชนีสำหรับกลุ่มโดย
Chloe

17
SELECT  DISTINCT *
      INTO tempdb.dbo.tmpTable
FROM myTable

TRUNCATE TABLE myTable
INSERT INTO myTable SELECT * FROM tempdb.dbo.tmpTable
DROP TABLE tempdb.dbo.tmpTable

5
การตัดทอนจะไม่ทำงานหากคุณมีรหัสต่างประเทศอ้างอิงถึง myTable
Sameer Alibhai

15

ฉันคิดว่าฉันจะแบ่งปันวิธีแก้ปัญหาของฉันเพราะมันทำงานภายใต้สถานการณ์พิเศษ ฉันกรณีของฉันตารางที่มีค่าซ้ำกันไม่มี foreign key (เนื่องจากค่าถูกทำซ้ำจาก db อื่น)

begin transaction
-- create temp table with identical structure as source table
Select * Into #temp From tableName Where 1 = 2

-- insert distinct values into temp
insert into #temp 
select distinct * 
from  tableName

-- delete from source
delete from tableName 

-- insert into source from temp
insert into tableName 
select * 
from #temp

rollback transaction
-- if this works, change rollback to commit and execute again to keep you changes!!

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


14

ข้อความค้นหานี้มีประสิทธิภาพที่ดีมากสำหรับฉัน:

DELETE tbl
FROM
    MyTable tbl
WHERE
    EXISTS (
        SELECT
            *
        FROM
            MyTable tbl2
        WHERE
            tbl2.SameValue = tbl.SameValue
        AND tbl.IdUniqueValue < tbl2.IdUniqueValue
    )

มันลบ 1M แถวในน้อยกว่า 30 วินาทีจากตาราง 2M (ซ้ำกัน 50%)


14

ใช้ CTE แนวคิดคือการเข้าร่วมในคอลัมน์อย่างน้อยหนึ่งคอลัมน์ที่ก่อให้เกิดการบันทึกซ้ำและลบสิ่งที่คุณต้องการ:

;with cte as (
    select 
        min(PrimaryKey) as PrimaryKey
        UniqueColumn1,
        UniqueColumn2
    from dbo.DuplicatesTable 
    group by
        UniqueColumn1, UniqueColumn1
    having count(*) > 1
)
delete d
from dbo.DuplicatesTable d 
inner join cte on 
    d.PrimaryKey > cte.PrimaryKey and
    d.UniqueColumn1 = cte.UniqueColumn1 and 
    d.UniqueColumn2 = cte.UniqueColumn2;

1
ฉันคิดว่าคุณไม่มี AND ในการเข้าร่วมของคุณ
Justin R.

13

อีกวิธีที่ง่ายสามารถพบได้ที่ลิงค์วางที่นี่ อันนี้ง่ายต่อการเข้าใจและดูเหมือนว่าจะมีประสิทธิภาพสำหรับปัญหาส่วนใหญ่ที่คล้ายกัน มันมีไว้สำหรับ SQL Server แต่แนวคิดที่ใช้นั้นมากกว่าที่ยอมรับได้

นี่คือส่วนที่เกี่ยวข้องจากหน้าที่เชื่อมโยง:

พิจารณาข้อมูลนี้:

EMPLOYEE_ID ATTENDANCE_DATE
A001    2011-01-01
A001    2011-01-01
A002    2011-01-01
A002    2011-01-01
A002    2011-01-01
A003    2011-01-01

ดังนั้นเราจะลบข้อมูลที่ซ้ำกันเหล่านั้นได้อย่างไร

ก่อนอื่นให้แทรกคอลัมน์ข้อมูลประจำตัวในตารางนั้นโดยใช้รหัสต่อไปนี้:

ALTER TABLE dbo.ATTENDANCE ADD AUTOID INT IDENTITY(1,1)  

ใช้รหัสต่อไปนี้เพื่อแก้ไข:

DELETE FROM dbo.ATTENDANCE WHERE AUTOID NOT IN (SELECT MIN(AUTOID) _
    FROM dbo.ATTENDANCE GROUP BY EMPLOYEE_ID,ATTENDANCE_DATE) 

1
"เข้าใจง่าย", "ดูเหมือนว่าจะมีประสิทธิภาพ" แต่ไม่ใช่คำที่เกี่ยวกับวิธีการที่ประกอบด้วยลองนึกภาพว่าลิงก์ไม่ถูกต้องการใช้งานใดจะรู้ว่าวิธีนั้นง่ายต่อการเข้าใจและมีประสิทธิภาพ โปรดลองเพิ่มส่วนสำคัญของคำอธิบายวิธีการในโพสต์ของคุณมิฉะนั้นจะไม่ได้รับคำตอบ
Andriy M

วิธีนี้มีประโยชน์สำหรับตารางที่คุณยังไม่ได้ระบุตัวตน บ่อยครั้งที่คุณต้องกำจัดรายการที่ซ้ำกันเพื่อกำหนดคีย์หลัก!
Jeff Davis

@JeffDavis - ROW_NUMBERรุ่นใช้งานได้ดีสำหรับกรณีนั้นโดยไม่จำเป็นต้องไปที่ความยาวของการเพิ่มคอลัมน์ใหม่ก่อนที่คุณจะเริ่ม
Martin Smith

12

นี่เป็นอีกบทความที่ดีในการที่ซ้ำกันลบ

มันอธิบายถึงสาเหตุที่ยาก: " SQL ยึดตามพีชคณิตเชิงสัมพันธ์และการทำซ้ำไม่สามารถเกิดขึ้นในพีชคณิตเชิงสัมพันธ์ได้เนื่องจากไม่อนุญาตให้ทำซ้ำในชุด "

วิธีแก้ปัญหาตาราง temp และสองตัวอย่าง mysql

ในอนาคตคุณจะป้องกันระดับฐานข้อมูลหรือจากมุมมองของแอปพลิเคชัน ฉันจะแนะนำระดับฐานข้อมูลเนื่องจากฐานข้อมูลของคุณควรรับผิดชอบในการรักษา Referential Integrity นักพัฒนาจะทำให้เกิดปัญหา;)


1
SQL ขึ้นอยู่กับหลายชุด แต่ถึงแม้ว่ามันจะขึ้นอยู่กับฉาก แต่ทูเปิลสองตัวนี้ (1, a) & (2, a) นั้นแตกต่างกัน
แอนดรู

12

โอ้แน่นอน ใช้ตารางชั่วคราว หากคุณต้องการคำแถลงเดียวที่ไม่ได้มีประสิทธิภาพมากนักซึ่ง "ทำงาน" คุณสามารถไปกับ:

DELETE FROM MyTable WHERE NOT RowID IN
    (SELECT 
        (SELECT TOP 1 RowID FROM MyTable mt2 
        WHERE mt2.Col1 = mt.Col1 
        AND mt2.Col2 = mt.Col2 
        AND mt2.Col3 = mt.Col3) 
    FROM MyTable mt)

โดยทั่วไปสำหรับแต่ละแถวในตารางตัวเลือกย่อยจะค้นหา RowID บนสุดของแถวทั้งหมดที่เหมือนกับแถวที่อยู่ในการพิจารณา ดังนั้นคุณจะจบลงด้วยรายการ RowIDs ที่แสดงแถว "ดั้งเดิม" ที่ไม่ซ้ำกัน


11

ฉันมีตารางที่ฉันต้องการรักษาแถวที่ไม่ซ้ำกัน ฉันไม่แน่ใจเรื่องความเร็วหรือประสิทธิภาพ

DELETE FROM myTable WHERE RowID IN (
  SELECT MIN(RowID) AS IDNo FROM myTable
  GROUP BY Col1, Col2, Col3
  HAVING COUNT(*) = 2 )

7
นี่ถือว่ามีมากที่สุด 1 ซ้ำ
Martin Smith

ทำไมHAVING COUNT(*) > 1ล่ะ
Philipp M

11

ใช้สิ่งนี้

WITH tblTemp as
(
SELECT ROW_NUMBER() Over(PARTITION BY Name,Department ORDER BY Name)
   As RowNumber,* FROM <table_name>
)
DELETE FROM tblTemp where RowNumber >1

10

วิธีอื่น ๆสร้างใหม่ตารางที่มีเขตข้อมูลเดียวกันและมีดัชนีที่ไม่ซ้ำกัน จากนั้นย้ายข้อมูลทั้งหมดจากตารางเก่าไปยังตารางใหม่ ละเว้น SQL Server โดยอัตโนมัติ (นอกจากนี้ยังมีตัวเลือกเกี่ยวกับสิ่งที่ต้องทำหากจะมีค่าซ้ำ: ละเว้น, อินเตอร์รัปต์หรือ sth) ค่าซ้ำกัน ดังนั้นเราจึงมีตารางเดียวกันโดยไม่มีแถวที่ซ้ำกัน หากคุณไม่ต้องการดัชนีที่ไม่ซ้ำหลังจากโอนข้อมูลคุณสามารถปล่อยมันได้

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


9

โดยใช้แบบสอบถามด้านล่างเราสามารถลบระเบียนที่ซ้ำกันตามคอลัมน์เดียวหรือหลายคอลัมน์ ข้อความค้นหาด้านล่างกำลังลบตามสองคอลัมน์ ชื่อตารางคือ: testingและชื่อคอลัมน์empno,empname

DELETE FROM testing WHERE empno not IN (SELECT empno FROM (SELECT empno, ROW_NUMBER() OVER (PARTITION BY empno ORDER BY empno) 
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1)
or empname not in
(select empname from (select empname,row_number() over(PARTITION BY empno ORDER BY empno) 
AS [ItemNumber] FROM testing) a WHERE ItemNumber > 1)

9
  1. สร้างตารางว่างใหม่ที่มีโครงสร้างเดียวกัน

  2. ดำเนินการค้นหาเช่นนี้

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) > 1
  3. จากนั้นดำเนินการค้นหานี้

    INSERT INTO tc_category1
    SELECT *
    FROM tc_category
    GROUP BY category_id, application_id
    HAVING count(*) = 1

9

นี่เป็นวิธีที่ง่ายที่สุดในการลบระเบียนที่ซ้ำกัน

 DELETE FROM tblemp WHERE id IN 
 (
  SELECT MIN(id) FROM tblemp
   GROUP BY  title HAVING COUNT(id)>1
 )

http://askme.indianyouth.info/details/how-to-dumplicate-record-from-table-in-using-sql-105


ทำไมมีคนมาประท้วงเรื่องนี้? หากคุณมี ID เดียวกันมากกว่าสองรายการสิ่งนี้จะไม่ทำงาน เขียนแทน: ลบจาก tblemp โดยที่ id ไม่ได้อยู่ใน (เลือก min (id) จากกลุ่ม tblemp ตามชื่อ)
crellee

7

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

SET ROWCOUNT 1 -- or set to number of rows to be deleted
delete from myTable where RowId = DuplicatedID
SET ROWCOUNT 0

7

จากระดับแอปพลิเคชัน (น่าเสียดาย) ฉันยอมรับว่าวิธีที่เหมาะสมในการป้องกันการทำซ้ำอยู่ในระดับฐานข้อมูลผ่านการใช้ดัชนีที่ไม่ซ้ำกัน แต่ใน SQL Server 2005 ดัชนีได้รับอนุญาตให้มีเพียง 900 ไบต์และเขต varchar (2048) ของฉันจะระเบิดออกไป

ฉันไม่รู้ว่ามันจะทำงานได้ดีแค่ไหน แต่ฉันคิดว่าคุณสามารถเขียนทริกเกอร์เพื่อบังคับใช้สิ่งนี้แม้ว่าคุณจะไม่สามารถทำได้ด้วยดัชนีโดยตรง สิ่งที่ต้องการ:

-- given a table stories(story_id int not null primary key, story varchar(max) not null)
CREATE TRIGGER prevent_plagiarism 
ON stories 
after INSERT, UPDATE 
AS 
    DECLARE @cnt AS INT 

    SELECT @cnt = Count(*) 
    FROM   stories 
           INNER JOIN inserted 
                   ON ( stories.story = inserted.story 
                        AND stories.story_id != inserted.story_id ) 

    IF @cnt > 0 
      BEGIN 
          RAISERROR('plagiarism detected',16,1) 

          ROLLBACK TRANSACTION 
      END 

นอกจากนี้ varchar (2048) ฟังดูน่าฟังสำหรับฉัน (บางสิ่งในชีวิตคือ 2048 ไบต์ แต่มันค่อนข้างผิดปกติ); มันควรจะไม่ varchar (สูงสุด) จริง ๆ ?


7

อีกวิธีในการทำเช่นนี้: -

DELETE A
FROM   TABLE A,
       TABLE B
WHERE  A.COL1 = B.COL1
       AND A.COL2 = B.COL2
       AND A.UNIQUEFIELD > B.UNIQUEFIELD 

แตกต่างจากคำตอบที่มีอยู่นี้ตั้งแต่วันที่ 20 สิงหาคม 2008 - stackoverflow.com/a/18934/692942
Lankymart

7
DELETE
FROM
    table_name T1
WHERE
    rowid > (
        SELECT
            min(rowid)
        FROM
            table_name T2
        WHERE
            T1.column_name = T2.column_name
    );

สวัสดี Teena คุณพลาดตารางชื่ออลิซ T1 หลังจากลบความคิดเห็นไม่เช่นนั้นจะมีข้อยกเว้นทางไวยากรณ์
Nagaraj M

6
CREATE TABLE car(Id int identity(1,1), PersonId int, CarId int)

INSERT INTO car(PersonId,CarId)
VALUES(1,2),(1,3),(1,2),(2,4)

--SELECT * FROM car

;WITH CTE as(
SELECT ROW_NUMBER() over (PARTITION BY personid,carid order by personid,carid) as rn,Id,PersonID,CarId from car)

DELETE FROM car where Id in(SELECT Id FROM CTE WHERE rn>1)

6

ฉันต้องการดูตัวอย่างแถวที่คุณกำลังจะลบและควบคุมแถวที่ซ้ำกันเพื่อให้ ดูhttp://developer.azurewebsites.net/2014/09/better-sql-group-by-find-duplicate-data/

with MYCTE as (
  SELECT ROW_NUMBER() OVER (
    PARTITION BY DuplicateKey1
                ,DuplicateKey2 -- optional
    ORDER BY CreatedAt -- the first row among duplicates will be kept, other rows will be removed
  ) RN
  FROM MyTable
)
DELETE FROM MYCTE
WHERE RN > 1
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.