ค้นหาแถวที่ซ้ำกันใน SQL Server


231

ฉันมีฐานข้อมูล SQL Server ขององค์กรและมีแถวที่ซ้ำกันจำนวนมาก ฉันต้องการเรียกใช้คำสั่ง select เพื่อจับสิ่งเหล่านี้และจำนวนของ dupes แต่กลับด้วยรหัสที่เกี่ยวข้องกับแต่ละองค์กร

คำสั่งเช่น:

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

จะคืนสิ่งที่ชอบ

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

แต่ฉันต้องการคว้ารหัสของพวกเขาด้วย มีวิธีการทำเช่นนี้? อาจจะชอบ

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

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

คำตอบ:


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
มีข้อ จำกัด ใด ๆ ในแบบสอบถามนี้หรือไม่ตัวอย่างเช่นถ้าจำนวนระเบียน 10 ล้านบวก
ไอ

3
@Steam คุณถูกต้อง: คำตอบนี้ไม่ได้มีประสิทธิภาพในฐานข้อมูลขนาดใหญ่ที่มีหลายล้านระเบียน ชอบ GroupBy / มีคำตอบที่ส่งโดย Aykut ซึ่งสามารถเพิ่มประสิทธิภาพได้ดีกว่าโดยฐานข้อมูล ข้อยกเว้นหนึ่งข้อ: ฉันขอแนะนำให้ใช้ Count (0) แทน Count (*) เพื่อทำให้สิ่งต่าง ๆ ง่ายขึ้น
Mike Christian

1
@ Mike - ทำไม Count (0) กับ Count (*)
KornMuffin

2
@KornMuffin ในการหวนกลับความคิดเห็นของฉันในการนับ () เป็นโมฆะ การใช้การประเมินผลที่ไม่เป็นโมฆะในการนับ () มีประโยชน์เฉพาะเมื่อคุณต้องการนับผลลัพธ์ที่ไม่เป็นโมฆะที่ส่งคืนโดยการรวมภายนอก มิฉะนั้นให้ใช้ Count (*) คำอธิบายที่ดีจะพบว่าที่นี่
Mike Christian

ใช้isnull()สำหรับคอลัมน์ที่ไม่มีค่าในonส่วน
Arif Ulusoy

92

คุณสามารถเรียกใช้แบบสอบถามต่อไปนี้และค้นหารายการที่ซ้ำกันด้วยmax(id)และลบแถวเหล่านั้น

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

แต่คุณจะต้องเรียกใช้แบบสอบถามนี้สองสามครั้ง


คุณต้องใช้งานทุกMAX( COUNT(*) ) - 1ครั้งซึ่งอาจเป็นไปได้
DerMike

1
hi เป็นวิธีของพวกเขาที่จะได้รับ id ทั้งหมดแทน max id เช่น 2 ฉันสามารถใช้ max และ min แต่สิ่งที่มากกว่า 2? @DerMike
Arijit Mukherjee

31

คุณสามารถทำได้เช่นนี้:

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

หากคุณต้องการส่งกลับเฉพาะระเบียนที่สามารถลบได้ (ปล่อยให้เป็นหนึ่งในแต่ละรายการ) คุณสามารถใช้:

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

แก้ไข: SQL Server 2000 ไม่มีฟังก์ชัน ROW_NUMBER () คุณสามารถใช้:

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

ประโยคแรกใช้งานได้ แต่ประโยคที่สองดูเหมือนจะไม่ทำงาน
xtine

SQL Server ดูเหมือนจะไม่สามารถรับรู้ row_number ()?
xtine

อ้า ... คุณมี SQL Server รุ่นเก่ากว่าหรือเปล่า ฉันเชื่อว่ามันถูกนำมาใช้ใน SQL Server 2005
Paul

3
ขอบคุณอีกครั้งทุกครั้งที่ฉันต้องทำสิ่งนี้ฉันมาที่นี่และรักคุณ
workabyte

9

โซลูชันที่ทำเครื่องหมายว่าถูกต้องไม่ได้ผลสำหรับฉัน แต่ฉันพบคำตอบที่ใช้งานได้ดีมาก: รับรายการแถวที่ซ้ำกันใน MySql

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

คุณจะได้รับจำนวนซ้ำในชุดผลลัพธ์ดังนั้นคุณจะต้องจัดการกับคนเหล่านั้นด้วย
Renan

1
หาก ID เป็นตัวเลขการตรวจสอบn1.id > n2.idจะป้องกันไม่ให้แต่ละคู่แสดงขึ้นสองครั้ง
ดาวใน

9

คุณสามารถลองสิ่งนี้ดีที่สุดสำหรับคุณ

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

วิธีใดก็ได้ที่จะได้รับ id ทั้งหมดในเครื่องหมายจุลภาคแยกหรือคอลัมน์ที่แตกต่างกัน
Arijit Mukherjee

6

หากคุณต้องการลบข้อมูลซ้ำ:

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]

สำหรับการค้นหาบันทึกที่ซ้ำกัน 1) การใช้ 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) โดยใช้ GroupBy

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

นี่เป็นวิธีแก้ปัญหาที่เร็วที่สุดที่นี่เมื่อเลือกข้อมูลมากกว่า 10m แถวนั่นคือ ขอบคุณ
Fandango68

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

ดังนั้นระเบียนที่มี rowum> 1 จะเป็นระเบียนที่ซ้ำกันในตารางของคุณ 'แบ่งพาร์ติชันโดย' กลุ่มแรกตามเรกคอร์ดแล้วซีเรียลไลซ์เหล่านี้โดยให้หมายเลขอนุกรม ดังนั้น rownum> 1 จะเป็นระเบียนที่ซ้ำกันซึ่งสามารถลบได้


ฉันชอบอันนี้เพราะมันช่วยให้คุณสามารถเพิ่มคอลัมน์อื่น ๆ ในส่วนเลือกคำสั่งด้านในได้อย่างง่ายดาย ดังนั้นหากคุณต้องการส่งคืนคอลัมน์อื่น ๆ จากตาราง 'องค์กร' คุณไม่จำเป็นต้องทำ 'กลุ่มตาม' ในคอลัมน์เหล่านั้น
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

duplicate rowsคุณมีวิธีการหลายอย่างสำหรับการเลือก

สำหรับโซลูชันของฉันอันดับแรกให้พิจารณาตารางนี้เป็นตัวอย่าง

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' );

วิธีแก้ปัญหาแรก:

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

โซลูชัน Secound: ใช้identityฟิลด์

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

และจุดสิ้นสุดของโซลูชันทั้งหมดใช้คำสั่งนี้

DROP TABLE #Employee

0

ฉันคิดว่าฉันรู้ว่าสิ่งที่คุณต้องการฉันจำเป็นต้องผสมผสานระหว่างคำตอบและฉันคิดว่าฉันได้ทางออกที่เขาต้องการ:

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

การมีรหัสสูงสุดจะให้ ID ของผู้เผยแพร่และต้นฉบับซึ่งเป็นสิ่งที่เขาขอ:

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)

มีเพียงเรื่องเศร้าที่คุณนำออกมาในแบบฟอร์มนี้

id , name , dubid , name

หวังว่ามันจะช่วยได้


0

สมมติว่าเรามีตาราง 'นักศึกษา' ตารางที่มี 2 คอลัมน์:

  • student_id int
  • student_name varchar

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

ตอนนี้เราต้องการเห็นระเบียนที่ซ้ำกันใช้แบบสอบถามนี้:

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

ฉันมีตัวเลือกที่ดีกว่าเพื่อรับระเบียนที่ซ้ำกันในตาราง

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

ผลลัพธ์ของแบบสอบถามด้านบนแสดงชื่อที่ซ้ำกันทั้งหมดที่มีรหัสนักศึกษาที่ไม่ซ้ำกันและจำนวนการเกิดซ้ำที่ซ้ำกัน

คลิกที่นี่เพื่อดูผลลัพธ์ของ sql



0

ฉันใช้สองวิธีในการค้นหาแถวที่ซ้ำกัน วิธีที่หนึ่งเป็นวิธีที่มีชื่อเสียงที่สุดที่ใช้กลุ่มโดยและมี วิธีที่ 2 คือการใช้ CTE - ตารางนิพจน์ทั่วไป

ตามที่ระบุไว้โดย @RedFilter วิธีนี้ก็ถูกต้องเช่นกัน หลายครั้งที่ฉันพบวิธี CTE ก็มีประโยชน์สำหรับฉันเช่นกัน

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

ในตัวอย่างด้านบนเรารวบรวมผลลัพธ์โดยค้นหาการเกิดซ้ำโดยใช้ ROW_NUMBER และ PARTITION BY จากนั้นเราจะนำไปใช้โดยที่ส่วนคำสั่งเพื่อเลือกเฉพาะแถวที่อยู่บนการนับซ้ำมากกว่า 1 ผลทั้งหมดจะถูกรวบรวมตาราง CTE และเข้าร่วมกับตารางองค์กร

ที่มา: CodoBee


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