在 SQL 表中查找重复值

Posted

技术标签:

【中文标题】在 SQL 表中查找重复值【英文标题】:Finding duplicate values in a SQL table 【发布时间】:2011-02-05 09:27:16 【问题描述】:

用一个字段很容易找到重复项:

SELECT email, 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

此查询将给出 John、Sam、Tom、Tom,因为他们都有相同的 email

但是,我想要获得具有相同 email name 的重复项。

也就是说,我想得到“汤姆”,“汤姆”。

我需要这个的原因:我犯了一个错误,允许插入重复的 nameemail 值。现在我需要删除/更改重复项,所以我需要先找到它们。

【问题讨论】:

我认为它不会让您在第一个示例中选择名称,因为它不在聚合函数中。 “匹配的电子邮件地址及其姓名的数量是多少”是一些棘手的逻辑...... 发现这不适用于 MSSQL 服务器,因为 SELECT 中的 name 字段。 【参考方案1】:

这个问题在上述所有答案中都得到了很好的回答。但我想列出所有可能的方式,我们可以通过各种方式来做到这一点,这可能会传达我们如何做到这一点的理解,并且寻求者可以选择最适合他/她需要的解决方案之一,因为这是其中之一最常见的查询 SQL 开发人员会遇到不同的业务用例,或者有时在面试中也会遇到。

创建示例数据

我将首先设置这个问题的一些示例数据。

Create table NewTable (id int, name varchar(10), email varchar(50))
INSERT  NewTable VALUES (1,'John','asd@asd.com')
INSERT  NewTable VALUES (2,'Sam','asd@asd.com')
INSERT  NewTable VALUES (3,'Tom','asd@asd.com')
INSERT  NewTable VALUES (4,'Bob','bob@asd.com')
INSERT  NewTable VALUES (5,'Tom','asd@asd.com')

1。按条款使用分组

SELECT
    name,email, COUNT(*) AS Occurence
    FROM NewTable
    GROUP BY name,email
    HAVING COUNT(*)>1

它是如何工作的:

GROUP BY 子句按 名称和电子邮件列。 然后,COUNT() 函数返回数字 每个组的出现次数(姓名、电子邮件)。 然后,HAVING 子句保持 仅重复组,即具有多个组的组 发生。

2。使用 CTE:

要为每个重复行返回整行,请使用公用表表达式 (CTE) 将上述查询的结果与 NewTable 表连接起来:

WITH cte AS (
    SELECT
        name, 
        email, 
        COUNT(*) occurrences
    FROM NewTable
    GROUP BY 
        name, 
        email
    HAVING COUNT(*) > 1
)
SELECT 
    t1.Id,
    t1.name, 
    t1.email
FROM  NewTable t1
    INNER JOIN cte ON 
        cte.name = t1.name AND 
        cte.email = t1.email
ORDER BY 
    t1.name, 
    t1.email;

3。使用 ROW_NUMBER() 函数

WITH cte AS (
    SELECT 
        name, 
        email, 
        ROW_NUMBER() OVER (
            PARTITION BY name,email
            ORDER BY name,email) rownum
    FROM 
        NewTable t1
) 
SELECT 
  * 
FROM 
    cte 
WHERE 
    rownum > 1;

它是如何工作的:

ROW_NUMBER()NewTable 表的行按nameemail 列中的值分配到分区中。重复行将在nameemail 列中具有重复值,但行号不同 外部查询删除每个组中的第一行。

现在我相信,您可以对如何查找重复项并应用逻辑在所有可能的情况下查找重复项有了正确的想法。 谢谢。

【讨论】:

【参考方案2】:

我想这会对你有所帮助

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

【讨论】:

【参考方案3】:

聚会有点晚了,但我找到了一个非常酷的解决方法来查找所有重复的 ID:

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

【讨论】:

似乎是一个语法糖变通。很好的发现。 请记住,GROUP_CONCAT 将在某个预定长度后停止,因此您可能无法获得所有 ids。【参考方案4】:

与其他答案相比,您可以查看包含所有列(如果有)的整个记录。在row_number 函数的PARTITION BY 部分中,选择所需的唯一/重复列。

SELECT  *
FROM    (
 SELECT a.*
 ,      Row_Number() OVER (PARTITION BY Name, Age ORDER BY Name) AS r
 FROM   Customers AS a
)       AS b
WHERE   r > 1;

当你想用 ALL 字段选择 ALL 重复记录时,你可以这样写

CREATE TABLE test (
        id      bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY
,       c1      integer
,       c2      text
,       d       date DEFAULT now()
,       v       text
);

INSERT INTO test (c1, c2, v) VALUES
(1, 'a', 'Select'),
(1, 'a', 'ALL'),
(1, 'a', 'multiple'),
(1, 'a', 'records'),
(2, 'b', 'in columns'),
(2, 'b', 'c1 and c2'),
(3, 'c', '.');
SELECT * FROM test ORDER BY 1;

SELECT  *
FROM    test
WHERE   (c1, c2) IN (
 SELECT c1, c2
 FROM   test
 GROUP  BY 1,2
 HAVING count(*) > 1
)
ORDER   BY 1;

在PostgreSQL 中测试。

【讨论】:

对 SELECT * 的细微改动帮助我解决了一个小时的搜索问题。我以前从未使用过 OVER(PARTITION BY。我对在 SQL 中有多少种方法可以做同样的事情感到惊讶!【参考方案5】:

您使用以下我使用的查询:

   select *
        FROM TABLENAME
        WHERE PrimaryCoumnID NOT IN
        (
            SELECT MAX(PrimaryCoumnID)
            FROM  TABLENAME
            GROUP BY AnyCoumnID
        );

【讨论】:

【参考方案6】:

请尝试

SELECT UserID, COUNT(UserID) 
FROM dbo.User
GROUP BY UserID
HAVING COUNT(UserID) > 1

【讨论】:

【参考方案7】:

如果您使用 Microsoft Access,这种方式可以工作:

CREATE TABLE users (id int, name varchar(10), email varchar(50));

INSERT INTO users VALUES (1, 'John', 'asd@asd.com');
INSERT INTO users VALUES (2, 'Sam', 'asd@asd.com');
INSERT INTO users VALUES (3, 'Tom', 'asd@asd.com');
INSERT INTO users VALUES (4, 'Bob', 'bob@asd.com');
INSERT INTO users VALUES (5, 'Tom', 'asd@asd.com');

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

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

感谢 Tancrede Chazallet 提供删除代码。

【讨论】:

【参考方案8】:

您也可以使用分析函数尝试此操作的另一种简单方法:

SELECT * from 

(SELECT name, email,

COUNT(name) OVER (PARTITION BY name, email) cnt 

FROM users)

WHERE cnt >1;

【讨论】:

你在邮件后面漏掉了一个逗号 :)【参考方案9】:

表结构:

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

解决方案 1:

SELECT *,
       COUNT(*)
FROM users t1
INNER JOIN users t2
WHERE t1.id > t2.id
  AND t1.name = t2.name
  AND t1.email=t2.email

解决方案 2:

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

【讨论】:

【参考方案10】:

这里最重要的是要有最快的功能。还应确定重复索引。自联接是一个不错的选择,但要获得更快的功能,最好先找到具有重复项的行,然后与原始表联接以查找重复行的 id。最后按除 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;

【讨论】:

【参考方案11】:
SELECT name, email,COUNT(email) 
FROM users 
WHERE email IN (
    SELECT email 
    FROM users 
    GROUP BY email 
    HAVING COUNT(email) > 1)

【讨论】:

你不能在没有GROUP BY的情况下使用COUNT,除非它指的是整个表。 没有 Group By 你使用了 COUNT 但在这里我写错了 COUNT【参考方案12】:

确切的代码会有所不同,具体取决于您是要查找重复行还是仅查找具有相同电子邮件和名称的不同 ID。如果 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

【讨论】:

【参考方案13】:
select id,name,COUNT(*) from user group by Id,Name having COUNT(*)>1

【讨论】:

【参考方案14】:

你可以试试这个

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

【讨论】:

【参考方案15】:

如何获取表中的重复记录

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

【讨论】:

【参考方案16】:

您可以使用 SELECT DISTINCT 关键字去除重复项。您还可以按姓名进行过滤,并将具有该姓名的每个人都放在一张桌子上。

【讨论】:

【参考方案17】:

从表中的重复记录中检查。

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

【讨论】:

【参考方案18】:

删除名称重复的记录

;WITH CTE AS    
(

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

DELETE FROM CTE WHERE T > 1

【讨论】:

有效吗?我怎么会在 Postgres 中得到这个错误'关系“cte”不存在'? CTE 也可以在 postgress sql 中使用。这是链接postgresqltutorial.com/postgresql-cte 你一定还缺少别的东西。【参考方案19】:

我们可以在这里使用它来处理聚合函数,如下所示

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 和 data 与 Count(*) 一起使用。因此,它将给出两列中具有超过一倍相同值的所有记录。

由于某种原因,我们错误地错过了在 SQL Server 表中添加任何约束,并且记录已在前端应用程序的所有列中重复插入。然后我们可以使用下面的查询从表中删除重复的查询。

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

这里我们取了原表的所有不同记录,并删除了原表的记录。我们再次将新表中的所有不同值插入到原始表中,然后删除新表。

【讨论】:

【参考方案20】:
SELECT
    name, email, COUNT(*)
FROM
    users
GROUP BY
    name, email
HAVING 
    COUNT(*) > 1

只需对两列进行分组。

注意:旧的 ANSI 标准是在 GROUP BY 中包含所有非聚合列,但这已经随着 "functional dependency" 的想法而改变:

在关系数据库理论中,函数依赖是数据库关系中两组属性之间的约束。也就是说,函数依赖是描述关系中属性之间关系的一种约束。

支持不一致:

最近的 PostgreSQL supports it. SQL Server(与 SQL Server 2017 一样)仍然需要 GROUP BY 中的所有非聚合列。 mysql 是不可预测的,你需要sql_mode=only_full_group_by: GROUP BY lname ORDER BY showing wrong results; Which is the least expensive aggregate function in the absence of ANY()(请参阅已接受答案中的 cmets)。 Oracle 还不够主流(警告:幽默,我不了解 Oracle)。

【讨论】:

@webXL WHERE 与单个记录一起使用 HAVING 与组一起使用 @gbn 是否可以在结果中包含 ID?之后删除这些重复项会更容易。 @user797717:你需要有 MIN(ID),然后如果 MIN(ID) 值不在最后,则删除 ID 值 如果任何列有空值怎么办? 非常感谢,是的,它确实可以在 Oracle 中工作,尽管我需要条件的唯一性,而不是 &gt;1 =1【参考方案21】:

试试这个:

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)

如果您想要 dups 的 ID,请使用:

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)

【讨论】:

* 表名区分大小写array(3) [0]=> string(5) "42000" [1]=> int(1064) [2]=> string(226) "You您的 SQL 语法有错误;请查看与您的 MySQL 服务器版本对应的手册,了解在第 1 行的 '(PARTITION BY y.employee_id, y.leave_type_id ) AS RowRank ' 附近使用的正确语法"【参考方案22】:

试试这个代码

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】:

这会从每组重复项中选择/删除除一条记录之外的所有重复记录。因此,删除会留下所有唯一记录 + 每组重复项中的一条记录。

选择重复项:

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

请注意大量记录,这可能会导致性能问题。

【讨论】:

删除查询出错 - 您不能在 FROM 子句中指定目标表“城市”进行更新 既没有表“城市”也没有更新子句。你的意思是?删除查询哪里出错了? “OP”是什么意思?【参考方案24】:

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

【讨论】:

【参考方案25】:

如果您想删除重复项,这里有一个比在三重子选择中查找偶数/奇数行更简单的方法:

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
)

更容易阅读和理解恕我直言

注意:唯一的问题是您必须执行请求,直到没有删除任何行,因为您每次只删除每个重复项中的 1 个

【讨论】:

美观易读;我想找到一种方法,一次删除多个重复的行。 这对我不起作用,因为我得到了You can't specify target table 'users' for update in FROM clause @Whitecat 似乎是一个简单的 MySQL 问题:***.com/questions/4429319/… 对我来说失败了。我得到:“DBD::CSV::st 执行失败:在 /Users/hornenj/perl5/perlbrew/perls/perl-5.26.0/lib/site_perl/5.26 的哈希元素中使用未初始化的值 $_[1]。 0/SQL/Eval.pm 第 43 行" 我认为 where 子句应该是“ u.name = u2.name AND u.email = u2.email AND (u.id > u2.id OR u2.id > u.id)”不是吗?【参考方案26】:

通过使用 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

【讨论】:

【参考方案27】:
select name, email
, case 
when ROW_NUMBER () over (partition by name, email order by name) > 1 then 'Yes'
else 'No'
end "duplicated ?"
from users

【讨论】:

只有代码的答案在 Stack Overflow 上不受欢迎,你能解释一下为什么这会回答这个问题吗? @RichBenner:我没有找到诸如结果中的每一行和每一行之类的响应,它告诉我们哪些都是重复的行,哪些不是一目了然,哪些不是分组依据, 因为如果我们想将此查询与任何其他查询 group by 组合不是一个好的选择。 在 select 语句中添加 Id 并过滤 duplicated ,它使您可以删除重复的 id 并继续每个。【参考方案28】:
SELECT * FROM users u where rowid = (select max(rowid) from users u1 where
u.email=u1.email);

【讨论】:

【参考方案29】:

这是我想出的简单方法。它使用公用表表达式 (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

【讨论】:

【参考方案30】:

这应该也行,也许试试看。

  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()

【讨论】:

以上是关于在 SQL 表中查找重复值的主要内容,如果未能解决你的问题,请参考以下文章

sql SQL查询使用单个值查找表中的重复项。

如何在 Oracle 的表中查找重复值?

从 SQL 表中查找部分和完全重复

如何用sql 语句查找一个表里的两个字段重复的记录

在具有重复行的 SQL Server 表中按组查找行号

sql查询去除重复值语句