การค้นหาค่าที่ซ้ำกันในตาราง SQL


1935

การค้นหารายการที่ซ้ำกันด้วยฟิลด์เดียวทำได้ง่าย:

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

ดังนั้นหากเรามีโต๊ะ

ID   NAME   EMAIL
1    John   asd@asd.com
2    Sam    asd@asd.com
3    Tom    asd@asd.com
4    Bob    bob@asd.com
5    Tom    asd@asd.com

แบบสอบถามนี้จะให้เราจอห์นแซมทอมทอมเพราะพวกเขามีเหมือนกันemailทั้งหมด

แต่สิ่งที่ผมต้องการคือการได้รับข้อมูลที่ซ้ำกันด้วยเหมือนกันและemail name

นั่นคือฉันต้องการได้รับ "ทอม", "ทอม"

เหตุผลที่ฉันต้องการสิ่งนี้: ฉันทำผิดพลาดและอนุญาตให้ใส่ค่าซ้ำnameและemailค่าได้ ตอนนี้ฉันต้องการลบ / เปลี่ยนรายการที่ซ้ำกันดังนั้นฉันต้องค้นหาก่อน


28
ฉันไม่คิดว่ามันจะให้คุณเลือกชื่อในตัวอย่างแรกของคุณเพราะมันไม่ได้อยู่ในฟังก์ชั่นรวม "อะไรคือสิ่งที่นับของการจับคู่ที่อยู่อีเมลและชื่อของพวกเขา" เป็นบางตรรกะหากิน ...
SXE

3
พบว่าสิ่งนี้ไม่ทำงานกับเซิร์ฟเวอร์ MSSQL เนื่องจากnameฟิลด์ใน SELECT
E. van Putten

สิ่งที่ฉันต้องการคือ id ของบันทึกพร้อมอีเมลซ้ำ
Marcos Di Paolo

คำตอบ:


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

เพียงจัดกลุ่มทั้งสองคอลัมน์

หมายเหตุ: มาตรฐาน ANSI เก่าคือการมีทุกคอลัมน์ที่ไม่รวมอยู่ในกลุ่มตาม แต่นี้มีการเปลี่ยนแปลงกับความคิดของ"พึ่งพาการทำงาน" :

ในทฤษฎีฐานข้อมูลเชิงสัมพันธ์การพึ่งพาฟังก์ชั่นเป็นข้อ จำกัด ระหว่างคุณสมบัติสองชุดในความสัมพันธ์จากฐานข้อมูล กล่าวอีกนัยหนึ่งการพึ่งพาการทำงานเป็นข้อ จำกัด ที่อธิบายความสัมพันธ์ระหว่างคุณลักษณะในความสัมพันธ์

การสนับสนุนไม่สอดคล้องกัน:


92
@webXL WHERE ทำงานได้กับระเบียนเดียว HAVING ทำงานร่วมกับกลุ่มได้
bjan

8
@gbn เป็นไปได้หรือไม่ที่จะรวมรหัสในผลลัพธ์ จากนั้นจะเป็นการง่ายกว่าที่จะลบรายการที่ซ้ำกันในภายหลัง
user797717

13
@ user797717: คุณต้องมี MIN (ID) แล้วลบค่า ID ที่ไม่ได้อยู่ในอันดับสุดท้ายหากค่า MIN (ID)
gbn

1
กรณีใดบ้างที่คอลัมน์ใดมีค่า Null
Ankit Dhingra

1
ขอบคุณมากสำหรับสิ่งนี้และใช่มันทำงานได้ใน Oracle แม้ว่าฉันต้องการสภาพที่เป็นเอกลักษณ์ดังนั้นแทนที่จะเป็น>1 =1
Bill Naylor

370

ลองนี้:

declare @YourTable table (id int, name varchar(10), email varchar(50))

INSERT @YourTable VALUES (1,'John','John-email')
INSERT @YourTable VALUES (2,'John','John-email')
INSERT @YourTable VALUES (3,'fred','John-email')
INSERT @YourTable VALUES (4,'fred','fred-email')
INSERT @YourTable VALUES (5,'sam','sam-email')
INSERT @YourTable VALUES (6,'sam','sam-email')

SELECT
    name,email, COUNT(*) AS CountOf
    FROM @YourTable
    GROUP BY name,email
    HAVING COUNT(*)>1

เอาท์พุท:

name       email       CountOf
---------- ----------- -----------
John       John-email  2
sam        sam-email   2

(2 row(s) affected)

ถ้าคุณต้องการ ID ของ dups ใช้สิ่งนี้:

SELECT
    y.id,y.name,y.email
    FROM @YourTable y
        INNER JOIN (SELECT
                        name,email, COUNT(*) AS CountOf
                        FROM @YourTable
                        GROUP BY name,email
                        HAVING COUNT(*)>1
                    ) dt ON y.name=dt.name AND y.email=dt.email

เอาท์พุท:

id          name       email
----------- ---------- ------------
1           John       John-email
2           John       John-email
5           sam        sam-email
6           sam        sam-email

(4 row(s) affected)

เพื่อลบรายการที่ซ้ำกันลอง:

DELETE d
    FROM @YourTable d
        INNER JOIN (SELECT
                        y.id,y.name,y.email,ROW_NUMBER() OVER(PARTITION BY y.name,y.email ORDER BY y.name,y.email,y.id) AS RowRank
                        FROM @YourTable y
                            INNER JOIN (SELECT
                                            name,email, COUNT(*) AS CountOf
                                            FROM @YourTable
                                            GROUP BY name,email
                                            HAVING COUNT(*)>1
                                        ) dt ON y.name=dt.name AND y.email=dt.email
                   ) dt2 ON d.id=dt2.id
        WHERE dt2.RowRank!=1
SELECT * FROM @YourTable

เอาท์พุท:

id          name       email
----------- ---------- --------------
1           John       John-email
3           fred       John-email
4           fred       fred-email
5           sam        sam-email

(4 row(s) affected)


72

หากคุณต้องการลบรายการที่ซ้ำกันนี่เป็นวิธีที่ง่ายกว่ามากในการทำมากกว่าการค้นหาแถวคู่ / คี่ลงในตัวเลือกย่อยสามรายการ:

SELECT id, name, email 
FROM users u, users u2
WHERE u.name = u2.name AND u.email = u2.email AND u.id > u2.id

และเพื่อลบ:

DELETE FROM users
WHERE id IN (
    SELECT id/*, name, email*/
    FROM users u, users u2
    WHERE u.name = u2.name AND u.email = u2.email AND u.id > u2.id
)

ง่ายต่อการอ่านและเข้าใจ IMHO

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


2
ดีและอ่านง่าย ฉันต้องการหาวิธีที่จะลบแถวที่ซ้ำกันหลายแถวในคราวเดียว
Dickon Reed

1
สิ่งนี้ไม่ทำงานสำหรับฉันตามที่ฉันได้รับYou can't specify target table 'users' for update in FROM clause
Whitecat

1
@ Whitecat ดูเหมือนว่าปัญหา MySQL ง่าย ๆ : stackoverflow.com/questions/4429319/…
AncAinu

1
ล้มเหลวสำหรับฉัน ฉันได้รับ: "DBD :: CSV :: st execute ล้มเหลว: การใช้ค่าเริ่มต้น $ _ [1] ในองค์ประกอบแฮชที่ /Users/hornenj/perl5/perlbrew/perls/perl-5.26.0/lib/site_perl/5.26 0 / SQL / Eval.pm บรรทัด 43 "
Nigel Horne

1
ฉันคิดว่าคำสั่งย่อยควรเป็น "u.name = u2.name AND u.email = u2.email AND (u.id> u2.id หรือ u2.id> u.id)" ใช่ไหม
GiveEmTheBoot

48

ลองทำสิ่งต่อไปนี้:

SELECT * FROM
(
    SELECT Id, Name, Age, Comments, Row_Number() OVER(PARTITION BY Name, Age ORDER By Name)
        AS Rank 
        FROM Customers
) AS B WHERE Rank>1

3
การเปลี่ยนแปลงเล็กน้อยใน SELECT * ช่วยฉันแก้ไขการค้นหาหนึ่งชั่วโมง ฉันไม่เคยใช้ OVER (PARTITION BY มาก่อน) ฉันไม่เคยหยุดที่จะประหลาดใจกับการทำสิ่งเดียวกันใน SQL หลายวิธี!
Joe Ruder


28

สายไปงานเลี้ยงเล็ก ๆ น้อย ๆ แต่ฉันพบวิธีแก้ปัญหาที่ยอดเยี่ยมจริงๆในการค้นหา ID ที่ซ้ำกันทั้งหมด:

SELECT GROUP_CONCAT( id )
FROM users
GROUP BY email
HAVING ( COUNT(email) > 1 )

2
ดูเหมือนว่าจะเป็นน้ำตาล syntax เชิงปฏิบัติ หาดี
Chef_Code

3
เก็บไว้ในใจว่าGROUP_CONCATจะหยุดหลังจากระยะเวลาที่กำหนดไว้บางอย่างเพื่อให้คุณอาจไม่ได้รับทั้งหมดids
v010dya


23

การเลือกนี้ / ลบระเบียนที่ซ้ำกันทั้งหมดยกเว้นหนึ่งระเบียนจากกลุ่มที่ซ้ำกันแต่ละกลุ่ม ดังนั้นการลบจะปล่อยระเบียนที่ไม่ซ้ำกันทั้งหมด + หนึ่งระเบียนจากแต่ละกลุ่มของรายการที่ซ้ำกัน

เลือกรายการซ้ำ:

SELECT *
FROM table
WHERE
    id NOT IN (
        SELECT MIN(id)
        FROM table
        GROUP BY column1, column2
);

ลบรายการซ้ำ:

DELETE FROM table
WHERE
    id NOT IN (
        SELECT MIN(id)
        FROM table
        GROUP BY column1, column2
);

ตระหนักถึงการบันทึกจำนวนมากก็สามารถทำให้เกิดปัญหาประสิทธิภาพการทำงาน


2
ข้อผิดพลาดในการลบแบบสอบถาม - คุณไม่สามารถระบุ 'เมือง' ของตารางเป้าหมายเพื่ออัปเดตในส่วนคำสั่ง
Ali Azhar

2
ไม่มี 'เมือง' ของตารางหรืออนุประโยคที่อัปเดต คุณหมายถึงอะไร ข้อผิดพลาดในแบบสอบถามลบอยู่ที่ไหน
Martin Silovský

2
มันทำงานอย่างไรกับข้อมูลของ OP
thoroc

3
"OP" หมายถึงอะไร
Martin Silovský

19

ในกรณีที่คุณทำงานกับ Oracle วิธีนี้จะดีกว่า:

create table my_users(id number, name varchar2(100), email varchar2(100));

insert into my_users values (1, 'John', 'asd@asd.com');
insert into my_users values (2, 'Sam', 'asd@asd.com');
insert into my_users values (3, 'Tom', 'asd@asd.com');
insert into my_users values (4, 'Bob', 'bob@asd.com');
insert into my_users values (5, 'Tom', 'asd@asd.com');

commit;

select *
  from my_users
 where rowid not in (select min(rowid) from my_users group by name, email);

15
select name, email
, case 
when ROW_NUMBER () over (partition by name, email order by name) > 1 then 'Yes'
else 'No'
end "duplicated ?"
from users

2
มีเพียงคำตอบของโค้ดที่ถูกอ่านบน Stack Overflow คุณสามารถอธิบายได้ไหมว่าทำไมคำตอบนี้ถึงตอบ
คนรวย Benner

2
@RichBenner: ฉันไม่พบการตอบสนองเช่นทุกแถว & ในผลลัพธ์และบอกเราว่าแถวใดซ้ำกันและแถวใดที่ไม่ได้อยู่ในแวบเดียวและไม่รวมกลุ่มเพราะถ้าเราต้องการรวมสิ่งนี้เข้าด้วยกัน แบบสอบถามกับกลุ่มแบบสอบถามอื่น ๆ โดยไม่ได้เป็นตัวเลือกที่ดี
Narendra

2
การเพิ่ม Id ลงในคำสั่ง select และการกรองเมื่อทำซ้ำจะทำให้คุณสามารถลบรหัสที่ซ้ำกันและเก็บไว้ในแต่ละรายการได้
แอนทอน Reinhold Bertrand

12

หากคุณต้องการดูว่ามีแถวที่ซ้ำกันในตารางของคุณหรือไม่ฉันใช้แบบสอบถามด้านล่าง:

create table my_table(id int, name varchar(100), email varchar(100));

insert into my_table values (1, 'shekh', 'shekh@rms.com');
insert into my_table values (1, 'shekh', 'shekh@rms.com');
insert into my_table values (2, 'Aman', 'aman@rms.com');
insert into my_table values (3, 'Tom', 'tom@rms.com');
insert into my_table values (4, 'Raj', 'raj@rms.com');


Select COUNT(1) As Total_Rows from my_table 
Select Count(1) As Distinct_Rows from ( Select Distinct * from my_table) abc 

11

นี่คือสิ่งที่ฉันคิดขึ้นมาง่ายๆ จะใช้นิพจน์ตารางทั่วไป (CTE) และหน้าต่างพาร์ติชัน (ฉันคิดว่าคุณสมบัติเหล่านี้อยู่ใน SQL 2008 และใหม่กว่า)

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

with cte (StudentId, Fname, LName, DOB, RowCnt)
as (
SELECT StudentId, FirstName, LastName, DateOfBirth as DOB, SUM(1) OVER (Partition By FirstName, LastName, DateOfBirth) as RowCnt
FROM tblStudent
)
SELECT * from CTE where RowCnt > 1
ORDER BY DOB, LName


10

เราจะนับค่าที่ซ้ำกันได้อย่างไร? ไม่ว่าจะเป็นการทำซ้ำ 2 ครั้งหรือมากกว่า 2. เพียงนับพวกเขาไม่ใช่กลุ่มที่ชาญฉลาด

ง่ายเหมือน

select COUNT(distinct col_01) from Table_01

2
คำถามนี้ทำงานอย่างไรสำหรับคำถามที่ถาม สิ่งนี้ทำไม่ให้แถวที่ซ้ำกันข้อมูลในหลายคอลัมน์ (เช่น "อีเมล" และ "ชื่อ") ในแถวที่แตกต่างกัน
Jeroen

10

โดยใช้ CTE เราสามารถหาค่าที่ซ้ำกันเช่นนี้

with MyCTE
as
(
select Name,EmailId,ROW_NUMBER() over(PARTITION BY EmailId order by id) as Duplicate from [Employees]

)
select * from MyCTE where Duplicate>1

9
 select emp.ename, emp.empno, dept.loc 
          from emp
 inner join dept 
          on dept.deptno=emp.deptno
 inner join
    (select ename, count(*) from
    emp
    group by ename, deptno
    having count(*) > 1)
 t on emp.ename=t.ename order by emp.ename
/

8

SELECT id, COUNT(id) FROM table1 GROUP BY id HAVING COUNT(id)>1;

ฉันคิดว่าสิ่งนี้จะทำงานได้อย่างถูกต้องเพื่อค้นหาค่าซ้ำ ๆ ในคอลัมน์ใดคอลัมน์หนึ่ง


6
สิ่งนี้ไม่ได้เพิ่มอะไรเลยกับคำตอบยอดนิยมและในทางเทคนิคก็ไม่ได้แตกต่างจากรหัส OP ที่โพสต์ในคำถาม
Jeroen


6

สิ่งนี้ควรใช้งานได้อาจลองใช้ดู

  Select * from Users a
            where EXISTS (Select * from Users b 
                where (     a.name = b.name 
                        OR  a.email = b.email)
                     and a.ID != b.id)

ดีเป็นพิเศษในกรณีของคุณหากคุณค้นหารายการซ้ำที่มีคำนำหน้าหรือการเปลี่ยนแปลงทั่วไปเช่นอีเมลใหม่ในอีเมล จากนั้นคุณสามารถใช้ replace () ที่คอลัมน์เหล่านี้


5

หากคุณต้องการค้นหาข้อมูลที่ซ้ำกัน (โดยหนึ่งหรือหลายเกณฑ์) และเลือกแถวจริง

with MYCTE as (
    SELECT DuplicateKey1
        ,DuplicateKey2 --optional
        ,count(*) X
    FROM MyTable
    group by DuplicateKey1, DuplicateKey2
    having count(*) > 1
) 
SELECT E.*
FROM MyTable E
JOIN MYCTE cte
ON E.DuplicateKey1=cte.DuplicateKey1
    AND E.DuplicateKey2=cte.DuplicateKey2
ORDER BY E.DuplicateKey1, E.DuplicateKey2, CreatedAt

http://developer.azurewebsites.net/2014/09/better-sql-group-by-find-duplicate-data/


4
SELECT name, email,COUNT(email) 
FROM users 
WHERE email IN (
    SELECT email 
    FROM users 
    GROUP BY email 
    HAVING COUNT(email) > 1)

คุณไม่สามารถใช้งานได้COUNTหากไม่มีGROUP BYยกเว้นจะอ้างถึงตารางทั้งหมด
RalfFriedl

ไม่มีกลุ่มคุณใช้ COUNT แต่ที่นี่ฉันทำผิดพลาดในการพิมพ์ COUNT
Mohammad Neamul Islam


3

ในการตรวจสอบจากบันทึกที่ซ้ำกันในตาราง

select * from users s 
where rowid < any 
(select rowid from users k where s.name = k.name and s.email = k.email);

หรือ

select * from users s 
where rowid not in 
(select max(rowid) from users k where s.name = k.name and s.email = k.email);

หากต้องการลบระเบียนที่ซ้ำกันในตาราง

delete from users s 
where rowid < any 
(select rowid from users k where s.name = k.name and s.email = k.email);

หรือ

delete from users s 
where rowid not in 
(select max(rowid) from users k where s.name = k.name and s.email = k.email);


1

เราสามารถใช้งานได้ที่นี่ซึ่งใช้งานได้กับฟังก์ชั่นรวมดังที่แสดงด้านล่าง

create table #TableB (id_account int, data int, [date] date)
insert into #TableB values (1 ,-50, '10/20/2018'),
(1, 20, '10/09/2018'),
(2 ,-900, '10/01/2018'),
(1 ,20, '09/25/2018'),
(1 ,-100, '08/01/2018')  

SELECT id_account , data, COUNT(*)
FROM #TableB
GROUP BY id_account , data
HAVING COUNT(id_account) > 1

drop table #TableB

ที่นี่เป็นสองฟิลด์ id_account และข้อมูลถูกใช้กับ Count (*) ดังนั้นจะให้ระเบียนทั้งหมดที่มีค่าเดียวกันมากกว่าหนึ่งครั้งในทั้งสองคอลัมน์

เรามีเหตุผลบางอย่างผิดพลาดเราพลาดที่จะเพิ่มข้อ จำกัด ใด ๆ ในตารางเซิร์ฟเวอร์ SQL และมีการแทรกระเบียนซ้ำกันในคอลัมน์ทั้งหมดด้วยแอปพลิเคชันส่วนหน้า จากนั้นเราสามารถใช้แบบสอบถามด้านล่างเพื่อลบแบบสอบถามที่ซ้ำกันออกจากตาราง

SELECT DISTINCT * INTO #TemNewTable FROM #OriginalTable
TRUNCATE TABLE #OriginalTable
INSERT INTO #OriginalTable SELECT * FROM #TemNewTable
DROP TABLE #TemNewTable

ที่นี่เราได้นำข้อมูลที่แตกต่างของตาราง orignal และลบบันทึกของตารางต้นฉบับ อีกครั้งเราใส่ค่าที่แตกต่างทั้งหมดจากตารางใหม่ไปยังตารางเดิมแล้วลบตารางใหม่



1

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

SELECT u.*
FROM users AS u
JOIN (SELECT username, email
      FROM users
      GROUP BY username, email
      HAVING COUNT(*)>1) AS w
ON u.username=w.username AND u.email=w.email
ORDER BY u.email;

0

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


0

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

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

ในกรณีหลังคุณจะใช้:

SELECT name, email, COUNT(DISTINCT id)
FROM users
GROUP BY name, email
HAVING COUNT(DISTINCT id) > 1
ORDER BY COUNT(DISTINCT id) DESC
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.