Tìm giá trị trùng lặp trong bảng SQL


1935

Thật dễ dàng để tìm các bản sao với một trường:

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

Vì vậy, nếu chúng ta có một bảng

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

Truy vấn này sẽ cung cấp cho chúng tôi John, Sam, Tom, Tom vì tất cả chúng đều giống nhau email.

Tuy nhiên, những gì tôi muốn là có được các bản sao với cùng email name .

Đó là, tôi muốn có được "Tom", "Tom".

Lý do tôi cần điều này: Tôi đã phạm sai lầm và cho phép chèn các giá trị namevà trùng lặp email. Bây giờ tôi cần loại bỏ / thay đổi các bản sao, vì vậy tôi cần tìm chúng trước.


28
Tôi không nghĩ rằng nó sẽ cho phép bạn chọn tên trong mẫu đầu tiên của bạn vì nó không nằm trong hàm tổng hợp. "Số lượng địa chỉ email phù hợp và tên của họ là gì" là một số logic phức tạp ...
sXe

3
Nhận thấy rằng điều này không hoạt động với máy chủ MSSQL vì nametrường trong CHỌN.
E. van Putten

cái tôi cần là id của các bản ghi với email trùng lặp
Marcos Di Paolo

Câu trả lời:


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

Đơn giản chỉ cần nhóm trên cả hai cột.

Lưu ý: tiêu chuẩn ANSI cũ hơn là có tất cả các cột không tổng hợp trong NHÓM THEO nhưng điều này đã thay đổi với ý tưởng về "sự phụ thuộc chức năng" :

Trong lý thuyết cơ sở dữ liệu quan hệ, một phụ thuộc chức năng là một ràng buộc giữa hai bộ thuộc tính trong một mối quan hệ từ cơ sở dữ liệu. Nói cách khác, sự phụ thuộc chức năng là một ràng buộc mô tả mối quan hệ giữa các thuộc tính trong một mối quan hệ.

Hỗ trợ không nhất quán:


92
@webXL WHERE hoạt động với một bản ghi HAVING hoạt động với nhóm
bjan

8
@gbn Có thể đưa Id vào kết quả không? Sau đó, sẽ dễ dàng hơn để xóa những bản sao đó sau đó.
dùng797717

13
@ user797717: bạn cần phải có MIN (ID) và sau đó xóa các giá trị ID không nằm trong giá trị cuối cùng nếu MIN (ID)
gbn

1
Điều gì về trường hợp bất kỳ cột nào có giá trị null?
Ankit D Breathra

1
Cảm ơn rất nhiều vì điều này, và vâng, nó hoạt động trong Oracle, mặc dù tôi cần sự duy nhất của điều kiện, thay vì>1 =1
Bill Naylor

370

thử cái này:

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

ĐẦU RA:

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

(2 row(s) affected)

nếu bạn muốn ID của dups sử dụng:

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

ĐẦU RA:

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

(4 row(s) affected)

để xóa các bản sao thử:

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

ĐẦU RA:

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

(4 row(s) affected)

127

Thử cái này:

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

72

Nếu bạn muốn xóa các bản sao, đây là cách đơn giản hơn nhiều so với việc phải tìm các hàng chẵn / lẻ vào một bộ ba phụ:

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

Và để xóa:

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
)

Dễ đọc và dễ hiểu hơn IMHO

Lưu ý: Vấn đề duy nhất là bạn phải thực hiện yêu cầu cho đến khi không có hàng nào bị xóa, vì mỗi lần bạn chỉ xóa 1 bản sao


2
Đẹp và dễ đọc; Tôi muốn tìm một cách xóa nhiều hàng trùng lặp trong một lần.
Dickon Reed

1
Điều này không hiệu quả với tôi khi tôi nhận đượcYou can't specify target table 'users' for update in FROM clause
Whitecat

1
@Whitecat có vẻ như là một vấn đề đơn giản của MySQL: stackoverflow.com/questions/4429319/
AncAinu

1
Thất bại cho tôi. Tôi nhận được: "DBD :: CSV :: st exec 0 / SQL / Eval.pm dòng 43 "
Nigel Horne

1
Tôi nghĩ rằng mệnh đề where nên là "u.name = u2.name AND u.email = u2.email AND (u.id> u2.id HOẶC u2.id> u.id)" phải không?
GiveEmTheBoot

48

Hãy thử như sau:

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
Một thay đổi nhỏ đối với CHỌN * đã giúp tôi giải quyết một tìm kiếm giờ. Tôi chưa bao giờ sử dụng OVER (phân vùng bằng cách trước khi tôi không bao giờ ngừng ngạc nhiên trước bao nhiêu cách để làm điều tương tự trong SQL.!
Joe Ruder

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

28

Đến bữa tiệc muộn một chút nhưng tôi đã tìm thấy một cách giải quyết tuyệt vời để tìm tất cả các ID trùng lặp:

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

2
Xuất hiện là một công việc đường cú pháp xung quanh. Đẹp tìm thấy.
Chef_Code

3
Hãy nhớ rằng nó GROUP_CONCATsẽ dừng lại sau một số chiều dài được xác định trước, vì vậy bạn có thể không nhận được tất cả các ids.
v010dya

24

thử mã này

WITH CTE AS

( SELECT Id, Name, Age, Comments, RN = ROW_NUMBER()OVER(PARTITION BY Name,Age ORDER BY ccn)
FROM ccnmaster )
select * from CTE 

23

Thao tác này sẽ chọn / xóa tất cả các bản ghi trùng lặp ngoại trừ một bản ghi từ mỗi nhóm bản sao. Vì vậy, việc xóa để lại tất cả các bản ghi duy nhất + một bản ghi từ mỗi nhóm trùng lặp.

Chọn trùng lặp:

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

Xóa các bản sao:

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

Hãy nhận biết số lượng lớn hơn của hồ sơ, nó có thể gây ra vấn đề hiệu suất.


2
Lỗi trong xóa truy vấn - Bạn không thể chỉ định bảng mục tiêu 'thành phố' để cập nhật trong mệnh đề TỪ
Ali Azhar

2
Không có bảng 'thành phố' cũng không cập nhật điều khoản. Ý anh là gì? Đâu là một lỗi trong truy vấn xóa?
Martin Silovský

2
Làm thế nào mà nó hoạt động với dữ liệu của OP?
ngực

3
"OP" có nghĩa là gì?
Martin Silovský

19

Trong trường hợp bạn làm việc với Oracle, cách này sẽ thích hợp hơn:

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
Mã chỉ có câu trả lời được tán thành trên Stack Overflow, bạn có thể giải thích tại sao câu trả lời này không?
Rich Benner

2
@RichBenner: Tôi không tìm thấy phản hồi như, mỗi & mọi hàng trong kết quả và cho chúng tôi biết tất cả các hàng trùng lặp và không nằm trong nháy mắt và không được nhóm theo, bởi vì nếu chúng tôi muốn kết hợp điều này truy vấn với bất kỳ nhóm truy vấn khác bởi không phải là một lựa chọn tốt.
Narendra

2
Thêm Id vào câu lệnh chọn và lọc trên bản sao, nó cung cấp cho bạn khả năng xóa các id trùng lặp và tiếp tục mỗi id.
Antoine Reinhold Bertrand

12

Nếu bạn muốn xem liệu có bất kỳ hàng trùng lặp nào trong bảng của mình không, tôi đã sử dụng bên dưới Truy vấn:

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

Đây là điều dễ dàng mà tôi nghĩ ra. Nó sử dụng biểu thức bảng chung (CTE) và cửa sổ phân vùng (tôi nghĩ các tính năng này có trong SQL 2008 trở lên).

Ví dụ này tìm thấy tất cả các sinh viên với tên và dob trùng lặp. Các trường bạn muốn kiểm tra trùng lặp sẽ đi vào mệnh đề QUÁ. Bạn có thể bao gồm bất kỳ trường nào khác bạn muốn trong phép chiếu.

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

Làm thế nào chúng ta có thể đếm các giá trị trùng lặp ?? hoặc nó được lặp lại 2 lần hoặc lớn hơn 2. chỉ cần đếm chúng, không phải nhóm khôn ngoan.

đơn giản như

select COUNT(distinct col_01) from Table_01

2
Làm thế nào điều này sẽ làm việc cho câu hỏi như được hỏi? Điều này không cung cấp các hàng trùng lặp thông tin trong nhiều cột (ví dụ: "email" và "name") trong các hàng khác nhau.
Jeroen

10

Bằng cách sử dụng CTE, chúng ta cũng có thể tìm thấy giá trị trùng lặp như thế này

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;

Tôi nghĩ rằng điều này sẽ hoạt động đúng để tìm kiếm các giá trị lặp đi lặp lại trong một cột cụ thể.


6
Điều này không hoàn toàn thêm bất cứ điều gì vào câu trả lời hàng đầu và về mặt kỹ thuật thậm chí không thực sự khác với mã OP được đăng trong câu hỏi.
Jeroen

7
SELECT * FROM users u where rowid = (select max(rowid) from users u1 where
u.email=u1.email);

6

Điều này cũng nên làm việc, có thể cho nó thử.

  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)

Đặc biệt tốt trong trường hợp của bạn Nếu bạn tìm kiếm các bản sao có một số loại tiền tố hoặc thay đổi chung như ví dụ tên miền mới trong thư. sau đó bạn có thể sử dụng thay thế () tại các cột này


5

Nếu bạn muốn tìm dữ liệu trùng lặp (theo một hoặc một vài tiêu chí) và chọn các hàng thực tế.

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-d repeatate-data /


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

Bạn không thể sử dụng COUNTmà không có GROUP BY, trừ khi nó đề cập đến toàn bộ bảng.
RalfFriedl

Không có nhóm Bởi bạn đã sử dụng COUNT nhưng ở đây tôi đã mắc lỗi đánh máy để viết COUNT
Mohammad Neamul Hồi giáo

3

Để xóa các bản ghi có tên trùng lặp

;WITH CTE AS    
(

    SELECT ROW_NUMBER() OVER (PARTITION BY name ORDER BY name) AS T FROM     @YourTable    
)

DELETE FROM CTE WHERE T > 1

3

Để kiểm tra từ bản ghi trùng lặp trong một bảng.

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

hoặc là

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

Để xóa bản ghi trùng lặp trong một bảng.

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

hoặc là

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

SELECT column_name,COUNT(*) FROM TABLE_NAME GROUP BY column1, HAVING COUNT(*) > 1;


1

Chúng ta có thể sử dụng có ở đây hoạt động trên các hàm tổng hợp như dưới đây

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

Ở đây là hai trường id_account và dữ liệu được sử dụng với Count (*). Vì vậy, nó sẽ cung cấp cho tất cả các bản ghi có nhiều hơn một lần giá trị trong cả hai cột.

Chúng tôi một số lý do nhầm lẫn chúng tôi đã bỏ lỡ để thêm bất kỳ ràng buộc nào trong bảng máy chủ SQL và các bản ghi đã được chèn trùng lặp trong tất cả các cột với ứng dụng front-end. Sau đó chúng ta có thể sử dụng truy vấn bên dưới để xóa truy vấn trùng lặp khỏi bảng.

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

Ở đây chúng tôi đã lấy tất cả các bản ghi riêng biệt của bảng gốc và xóa các bản ghi của bảng gốc. Một lần nữa chúng tôi chèn tất cả các giá trị riêng biệt từ bảng mới vào bảng gốc và sau đó xóa bảng mới.


1

Bạn có thể muốn thử điều này

SELECT NAME, EMAIL, COUNT(*)
FROM USERS
GROUP BY 1,2
HAVING COUNT(*) > 1

1

Điều quan trọng nhất ở đây là có chức năng nhanh nhất. Cũng cần xác định các chỉ số trùng lặp. Tự tham gia là một tùy chọn tốt nhưng để có chức năng nhanh hơn, tốt nhất là trước tiên hãy tìm các hàng có trùng lặp và sau đó tham gia với bảng gốc để tìm id của các hàng trùng lặp. Cuối cùng, sắp xếp theo bất kỳ cột nào ngoại trừ id để có các hàng trùng lặp gần nhau.

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

Bạn có thể sử dụng từ khóa CHỌN DISTINCT để loại bỏ các bản sao. Bạn cũng có thể lọc theo tên và nhận mọi người có tên đó trên bàn.


0

Mã chính xác sẽ khác nhau tùy thuộc vào việc bạn muốn tìm các hàng trùng lặp hay chỉ các id khác nhau có cùng email và tên. Nếu id là khóa chính hoặc có một ràng buộc duy nhất thì sự khác biệt này không tồn tại, nhưng câu hỏi không chỉ rõ điều này. Trong trường hợp trước, bạn có thể sử dụng mã được đưa ra trong một số câu trả lời khác:

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

Trong trường hợp sau, bạn sẽ sử dụng:

SELECT name, email, COUNT(DISTINCT id)
FROM users
GROUP BY name, email
HAVING COUNT(DISTINCT id) > 1
ORDER BY COUNT(DISTINCT id) DESC
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.