Tìm các hàng trùng lặp trong SQL Server


231

Tôi có một cơ sở dữ liệu SQL Server của các tổ chức và có nhiều hàng trùng lặp. Tôi muốn chạy một câu lệnh chọn để lấy tất cả những thứ này và số lượng bản sao, nhưng cũng trả về các id được liên kết với mỗi tổ chức.

Một tuyên bố như:

SELECT     orgName, COUNT(*) AS dupes  
FROM         organizations  
GROUP BY orgName  
HAVING      (COUNT(*) > 1)

Sẽ trả lại một cái gì đó như

orgName        | dupes  
ABC Corp       | 7  
Foo Federation | 5  
Widget Company | 2 

Nhưng tôi cũng muốn lấy ID của họ. Có cách nào để làm điều này? Có lẽ giống như một

orgName        | dupeCount | id  
ABC Corp       | 1         | 34  
ABC Corp       | 2         | 5  
...  
Widget Company | 1         | 10  
Widget Company | 2         | 2  

Lý do là cũng có một bảng người dùng riêng liên kết với các tổ chức này và tôi muốn thống nhất chúng (do đó loại bỏ các bản sao để người dùng liên kết với cùng một tổ chức thay vì các bản sao đôi). Nhưng tôi muốn một phần theo cách thủ công để tôi không làm hỏng bất cứ điều gì, nhưng tôi vẫn cần một tuyên bố trả lại ID của tất cả các bản dupe để tôi có thể xem qua danh sách người dùng.

Câu trả lời:


313
select o.orgName, oc.dupeCount, o.id
from organizations o
inner join (
    SELECT orgName, COUNT(*) AS dupeCount
    FROM organizations
    GROUP BY orgName
    HAVING COUNT(*) > 1
) oc on o.orgName = oc.orgName

4
Có bất kỳ giới hạn nào trong truy vấn này không, ví dụ nếu số lượng hồ sơ là 10 triệu cộng?
Steam

3
@Steam Bạn đã đúng: câu trả lời này không hiệu quả trong cơ sở dữ liệu lớn hơn với hàng triệu bản ghi. Thích GroupBy / Có câu trả lời được gửi bởi Aykut, có thể được tối ưu hóa tốt hơn bởi cơ sở dữ liệu. Một ngoại lệ: Tôi đề nghị sử dụng Count (0) thay vì Count (*), để đơn giản hóa mọi thứ.
Mike Christian

1
@Mike - tại sao Đếm (0) so với Đếm (*)?
KornMuffin

2
@KornMuffin Nhìn lại, nhận xét của tôi về Count () là không có giá trị. Sử dụng đánh giá không null trong Count () chỉ hữu ích khi bạn muốn đếm kết quả không null được trả về bởi một phép nối ngoài. Nếu không, sử dụng Đếm (*). Một lời giải thích tuyệt vời được tìm thấy ở đây .
Mike Christian

sử dụng isnull()cho các cột không có giá trị trên onphần
Arif Ulusoy

92

Bạn có thể chạy truy vấn sau và tìm các bản sao với max(id)và xóa các hàng đó.

SELECT orgName, COUNT(*), Max(ID) AS dupes 
FROM organizations 
GROUP BY orgName 
HAVING (COUNT(*) > 1)

Nhưng bạn sẽ phải chạy truy vấn này một vài lần.


Bạn phải chạy nó chính xác MAX( COUNT(*) ) - 1lần, điều này vẫn có thể khả thi.
DerMike

1
hi là cách nào để lấy tất cả id thay vì id tối đa như 2 tôi có thể sử dụng max và min nhưng còn hơn 2 thì sao? @DerMike
Arijit Mukherjee

31

Bạn có thể làm như thế này:

SELECT
    o.id, o.orgName, d.intCount
FROM (
     SELECT orgName, COUNT(*) as intCount
     FROM organizations
     GROUP BY orgName
     HAVING COUNT(*) > 1
) AS d
    INNER JOIN organizations o ON o.orgName = d.orgName

Nếu bạn muốn trả lại chỉ các bản ghi có thể bị xóa (để lại một trong mỗi bản ghi), bạn có thể sử dụng:

SELECT
    id, orgName
FROM (
     SELECT 
         orgName, id,
         ROW_NUMBER() OVER (PARTITION BY orgName ORDER BY id) AS intRow
     FROM organizations
) AS d
WHERE intRow != 1

Chỉnh sửa: SQL Server 2000 không có chức năng ROW_NUMBER (). Thay vào đó, bạn có thể sử dụng:

SELECT
    o.id, o.orgName, d.intCount
FROM (
     SELECT orgName, COUNT(*) as intCount, MIN(id) AS minId
     FROM organizations
     GROUP BY orgName
     HAVING COUNT(*) > 1
) AS d
    INNER JOIN organizations o ON o.orgName = d.orgName
WHERE d.minId != o.id

Câu lệnh đầu tiên hoạt động, nhưng câu lệnh thứ hai dường như không hoạt động.
xtine

SQL Server dường như không thể nhận ra row_number ()?
xtine

À ... bạn có phiên bản SQL Server cũ hơn không? Tôi tin rằng nó đã được giới thiệu trong SQL Server 2005.
Paul

3
cảm ơn một lần nữa, mỗi khi tôi cần làm điều này tôi lại đến đây và YÊU BẠN
workabyte

9

Giải pháp được đánh dấu là không đúng với tôi, nhưng tôi thấy câu trả lời này rất hiệu quả: Nhận danh sách các hàng trùng lặp trong MySql

SELECT n1.* 
FROM myTable n1
INNER JOIN myTable n2 
ON n2.repeatedCol = n1.repeatedCol
WHERE n1.id <> n2.id

Bạn sẽ nhận được rất nhiều bản sao trong tập kết quả, vì vậy bạn cũng sẽ phải đối phó với những bản sao đó.
Renan

1
Nếu id là số, việc kiểm tra n1.id > n2.idsẽ ngăn mỗi cặp hiển thị hai lần.
bắt đầu

9

Bạn có thể thử điều này, nó là tốt nhất cho bạn

 WITH CTE AS
    (
    SELECT *,RN=ROW_NUMBER() OVER (PARTITION BY orgName ORDER BY orgName DESC) FROM organizations 
    )
    select * from CTE where RN>1
    go

bất kỳ cách nào để có được tất cả id trong dấu phẩy hoặc các cột khác nhau
Arijit Mukherjee

6

Nếu bạn muốn xóa trùng lặp:

WITH CTE AS(
   SELECT orgName,id,
       RN = ROW_NUMBER()OVER(PARTITION BY orgName ORDER BY Id)
   FROM organizations
)
DELETE FROM CTE WHERE RN > 1

6
select * from [Employees]

Để tìm bản ghi trùng lặp 1) Sử dụng CTE

with mycte
as
(
select Name,EmailId,ROW_NUMBER() over(partition by Name,EmailId order by id) as Duplicate from [Employees]
)
select * from mycte

2) Bằng cách sử dụng GroupBy

select Name,EmailId,COUNT(name) as Duplicate from  [Employees] group by Name,EmailId 

Đây là giải pháp nhanh nhất ở đây, khi CHỌN dữ liệu trên 10m hàng. Cảm ơn
Fandango68

4
Select * from (Select orgName,id,
ROW_NUMBER() OVER(Partition By OrgName ORDER by id DESC) Rownum
From organizations )tbl Where Rownum>1

Vì vậy, các bản ghi có hàng> 1 sẽ là các bản ghi trùng lặp trong bảng của bạn. 'Phân vùng theo' nhóm đầu tiên theo các bản ghi và sau đó tuần tự hóa chúng bằng cách cung cấp cho chúng số nos nối tiếp. Vì vậy, rownum> 1 sẽ là các bản ghi trùng lặp có thể bị xóa như vậy.


Tôi thích cái này vì nó cho phép bạn dễ dàng thêm nhiều cột hơn trong mệnh đề select bên trong. Vì vậy, nếu bạn muốn trả về các cột khác từ bảng 'Tổ chức', bạn không phải thực hiện một 'nhóm bằng' trên các cột đó.
Gwasshoppa


2
select a.orgName,b.duplicate, a.id
from organizations a
inner join (
    SELECT orgName, COUNT(*) AS duplicate
    FROM organizations
    GROUP BY orgName
    HAVING COUNT(*) > 1
) b on o.orgName = oc.orgName
group by a.orgName,a.id

1
select orgname, count(*) as dupes, id 
from organizations
where orgname in (
    select orgname
    from organizations
    group by orgname
    having (count(*) > 1)
)
group by orgname, id

1

Bạn có một số cách để chọn duplicate rows.

đối với các giải pháp của tôi, trước tiên hãy xem xét bảng này

CREATE TABLE #Employee
(
ID          INT,
FIRST_NAME  NVARCHAR(100),
LAST_NAME   NVARCHAR(300)
)

INSERT INTO #Employee VALUES ( 1, 'Ardalan', 'Shahgholi' );
INSERT INTO #Employee VALUES ( 2, 'name1', 'lname1' );
INSERT INTO #Employee VALUES ( 3, 'name2', 'lname2' );
INSERT INTO #Employee VALUES ( 2, 'name1', 'lname1' );
INSERT INTO #Employee VALUES ( 3, 'name2', 'lname2' );
INSERT INTO #Employee VALUES ( 4, 'name3', 'lname3' );

Giải pháp đầu tiên:

SELECT DISTINCT *
FROM   #Employee;

WITH #DeleteEmployee AS (
                     SELECT ROW_NUMBER()
                            OVER(PARTITION BY ID, First_Name, Last_Name ORDER BY ID) AS
                            RNUM
                     FROM   #Employee
                 )

SELECT *
FROM   #DeleteEmployee
WHERE  RNUM > 1

SELECT DISTINCT *
FROM   #Employee

Giải pháp Secound: Sử dụng identitylĩnh vực

SELECT DISTINCT *
FROM   #Employee;

ALTER TABLE #Employee ADD UNIQ_ID INT IDENTITY(1, 1)

SELECT *
FROM   #Employee
WHERE  UNIQ_ID < (
    SELECT MAX(UNIQ_ID)
    FROM   #Employee a2
    WHERE  #Employee.ID = a2.ID
           AND #Employee.FIRST_NAME = a2.FIRST_NAME
           AND #Employee.LAST_NAME = a2.LAST_NAME
)

ALTER TABLE #Employee DROP COLUMN UNIQ_ID

SELECT DISTINCT *
FROM   #Employee

và kết thúc tất cả các giải pháp sử dụng lệnh này

DROP TABLE #Employee

0

Tôi nghĩ rằng tôi biết những gì bạn cần tôi cần phải trộn lẫn giữa các câu trả lời và tôi nghĩ rằng tôi đã có giải pháp anh ấy muốn:

select o.id,o.orgName, oc.dupeCount, oc.id,oc.orgName
from organizations o
inner join (
    SELECT MAX(id) as id, orgName, COUNT(*) AS dupeCount
    FROM organizations
    GROUP BY orgName
    HAVING COUNT(*) > 1
) oc on o.orgName = oc.orgName

có id tối đa sẽ cung cấp cho bạn id của người cộng hòa và một trong những bản gốc đó là những gì anh ta yêu cầu:

id org name , dublicate count (missing out in this case) 
id doublicate org name , doub count (missing out again because does not help in this case)

điều đáng buồn duy nhất bạn nhận được nó đưa ra trong hình thức này

id , name , dubid , name

hy vọng nó vẫn giúp


0

Giả sử chúng ta có bảng bảng 'Học sinh' với 2 cột:

  • student_id int
  • student_name varchar

    Records:
    +------------+---------------------+
    | student_id | student_name        |
    +------------+---------------------+
    |        101 | usman               |
    |        101 | usman               |
    |        101 | usman               |
    |        102 | usmanyaqoob         |
    |        103 | muhammadusmanyaqoob |
    |        103 | muhammadusmanyaqoob |
    +------------+---------------------+

Bây giờ chúng tôi muốn xem các bản ghi trùng lặp Sử dụng truy vấn này:

select student_name,student_id ,count(*) c from student group by student_id,student_name having c>1;

+---------------------+------------+---+
| student_name        | student_id | c |
+---------------------+------------+---+
| usman               |        101 | 3 |
| muhammadusmanyaqoob |        103 | 2 |
+---------------------+------------+---+

0

Tôi có một tùy chọn tốt hơn để có được các bản ghi trùng lặp trong một bảng

SELECT x.studid, y.stdname, y.dupecount
FROM student AS x INNER JOIN
(SELECT a.stdname, COUNT(*) AS dupecount
FROM student AS a INNER JOIN
studmisc AS b ON a.studid = b.studid
WHERE (a.studid LIKE '2018%') AND (b.studstatus = 4)
GROUP BY a.stdname
HAVING (COUNT(*) > 1)) AS y ON x.stdname = y.stdname INNER JOIN
studmisc AS z ON x.studid = z.studid
WHERE (x.studid LIKE '2018%') AND (z.studstatus = 4)
ORDER BY x.stdname

Kết quả của truy vấn trên cho thấy tất cả các tên trùng lặp với id sinh viên duy nhất và số lần xuất hiện trùng lặp

Nhấn vào đây để xem kết quả của sql


0
 /*To get duplicate data in table */

 SELECT COUNT(EmpCode),EmpCode FROM tbl_Employees WHERE Status=1 
  GROUP BY EmpCode HAVING COUNT(EmpCode) > 1

0

Tôi sử dụng hai phương pháp để tìm các hàng trùng lặp. Phương pháp thứ 1 là phương pháp nổi tiếng nhất sử dụng nhóm bởi và có. Phương pháp thứ 2 là sử dụng CTE - Biểu thức bảng chung .

Như @RedFilter đã đề cập theo cách này cũng đúng. Nhiều lần tôi thấy phương pháp CTE cũng hữu ích cho tôi.

WITH TempOrg (orgName,RepeatCount)
AS
(
SELECT orgName,ROW_NUMBER() OVER(PARTITION by orgName ORDER BY orgName) 
AS RepeatCount
FROM dbo.organizations
)
select t.*,e.id from organizations   e
inner join TempOrg t on t.orgName= e.orgName
where t.RepeatCount>1

Trong ví dụ trên, chúng tôi đã thu thập kết quả bằng cách tìm sự xuất hiện lặp lại bằng ROW_NUMBER và PHẦN THAM GIA. Sau đó, chúng tôi đã áp dụng mệnh đề where để chỉ chọn các hàng đang lặp lại đếm nhiều hơn 1. Tất cả kết quả được thu thập bảng CTE và được nối với bảng Tổ chức.

Nguồn: CodoBee


-2

Thử

SELECT orgName, id, count(*) as dupes
FROM organizations
GROUP BY orgName, id
HAVING count(*) > 1;
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.