数据库常用操作之删除重复数据且仅保留一条

本文介绍如何在MySQL和Oracle数据库中查询并删除重复数据的方法。通过多种SQL脚本示例,包括单字段和多字段操作,帮助读者掌握不同场景下处理重复数据的技术。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

单字段操作

查询全部重复的数据

MySQL实现

  • sql脚本

    
        Select * FromWhere 重复字段 In (Select 重复字段 FromGroup By 重复字段 Having Count(*)>1)
    
        
        
    

Oracle实现

查询表中重复数据(id最小除外)

MySQL实现

  • sql脚本

    
        -- 方式一
        SELECT 
            *
        FROMWHERE
            主键 NOT IN (
                SELECT
                    t.主键别名
                FROM
                    (
                        SELECT
                            MIN(主键) AS 主键别名
                        FROMGROUP BY
                            重复字段
                    ) t
            )
    
    
        -- 方式二
        SELECT * FROMAS ta WHERE ta.唯一键 <> ( SELECT min( tb.唯一键 ) FROMAS tb WHERE ta.判断重复的列 = tb.判断重复的列 );
    
    
        -- 方式三(使用exists 替代 in)
        SELECT 
            *
        FROM
            表 t1
        WHERE
            NOT EXISTS (
                SELECT
                    1
                FROM
                    (
                        SELECT
                            MIN(主键) AS 主键别名
                        FROMGROUP BY
                            重复字段
                    ) t2
                WHERE 
                    t2.主键别名 = t1.主键
            )
    
    
  • 执行结果分析

    方式二查询的数据会比方式一少一些,因为重复的列可能存在为空的情况,导致关联失败,无法查询,mysql可以使用ifnull()函数将对应重复字段替换成指定字符,如0,Oracle使用nvl()函数。

    
        SELECT * FROMAS ta WHERE ta.唯一键 <> ( SELECT min( tb.唯一键 ) FROMAS tb WHERE ifnull(ta.判断重复的列,'0') = ifnull(tb.判断重复的列,'0') );
    
    
    

    在判断null==null时结果为false;故查询数据为空。Oracle中在处理千万数据之下时,可以使用内部函数nvl(null,‘0’)赋相同值判断两个空值。如果是千万以上数据建议使用isnull判断。目的是消除nvl对索引的影响。

删除表中多余重复数据并且只留1条

MySQL实现

  • 方式一

    
        delete fromwhere 主键 not in (select min(主键) as 主键别名 fromgroup by 重复字段);
    
    
  • 注意事项

    方式一执行会出现如下错误:[Err] 1093 - You can’t specify target table ‘dept’ for update in FROM clause

    原因是:更新这个表的同时又查询了这个表,查询这个表的同时又去更新了这个表,可以理解为死锁。mysql不支持这种更新查询同一张表的操作

    解决办法:把要更新的几列数据查询出来做为一个第三方表,然后筛选更新

  • 方式二(推荐使用)

    
    
        delete fromwhere 主键 not in (select 表别名.主键别名 from (select min(主键) as 主键别名 fromgroup by 重复字段) 表别名) ;
    
        -- 删除username相同的重复数据,保留id最小的用户信息
        delete from 
            student 
        where
            username in (select username from ( select username from student group by username having count(username)>1) a)
        and 
            id not in ( select id from (select min(id) as id from student group by username having count(username)>1 ) b)
    
        -- 简写
        delete from student where id not in (select id from (select min(id) as id from student group by username) b);
    
    
    
  • 方式三

    
        DELETE 
        FROM
            table_name AS ta 
        WHERE
            ta.唯一键 <> (
        SELECT
            t.maxid 
        FROM
            ( SELECT max( tb.唯一键 ) AS maxid FROM table_name AS tb WHERE ta.判断重复的列 = tb.判断重复的列 ) t 
            );
    

Oracle实现

多字段操作

删除表中多余重复数据并且只留1条

  • 方式一

    
        DELETE
        FROM
            dept
        WHERE
            (dname, db_source) IN (
                SELECT
                    t.dname,
                    t.db_source
                FROM
                    (
                        SELECT
                            dname,
                            db_source
                        FROM
                            dept
                        GROUP BY
                            dname,
                            db_source
                        HAVING
                            count(1) > 1
                    ) t
            )
        AND deptno NOT IN (
            SELECT
                dt.mindeptno
            FROM
                (
                    SELECT
                        min(deptno) AS mindeptno
                    FROM
                        dept
                    GROUP BY
                        dname,
                        db_source
                    HAVING
                        count(1) > 1
                ) dt
        )
    
    
    

其他

  • count(*)与count(1)与count(列名)异同

    count(*)与count(1)其实没有什么差别,用哪个都可以

    count(*)与count(列名)的区别

    count(*)将返回表格中所有存在的行的总数包括值为null的行,然而count(列名)将返回表格中除去null以外的所有行的总数(有默认值的列也会被计入)

  • in 和 exists关键字

    小表驱动大表用IN,大表驱动小表用EXISTS。IN适合的情况是外表数据量小的情况,而不是外表数据大的情况,因为IN会遍历外表的全部数据,假设a表100条,b表10000条那么遍历次数就是100*10000次,而exists则是执行100次去判断a表中的数据是否在b表中存在,它只执行了a.length次数。至于哪一个效率高是要看情况的,因为in是在内存中比较的,而exists则是进行数据库查询操作的

参考链接

  • mysql删除重复记录并且只保留一条

    https://blog.csdn.net/n950814abc/article/details/82284838

  • SQL IN与EXISTS的执行效率比较

    https://www.cnblogs.com/AllUserBegin/p/3513084.html

  • sql优化–in和exists效率

    https://www.cnblogs.com/weifeng123/p/9530758.html

<think>嗯,用户的问题是关于在Oracle数据库删除重复数据同时保留一条记录的最佳实践。首先,我需要回顾一下Oracle中处理重复数据常用方法。根据之前学过的知识,记得有几种方法,比如使用ROWID或者ROW_NUMBER()窗口函数。还有临时表的方法,但可能不太高效,尤其是对于大表来说。 用户提到要保留一条记录,通常这意味着保留一条特定的记录,比如最新或最旧的。这时候ROW_NUMBER()函数可能比较适合,因为它可以根据某个排序条件给每行编号,然后选择保留编号为1的行。例如,按时间戳降序排序,保留最新的那条。 另外,记得Oracle的ROWID是每行的唯一物理地址,可以用来直接定位重复数据中的某一行。比如,对于每组重复的记录,保留ROWID最小的那条,删除其他的。不过这种方法可能需要自连接表,或者在子查询中找到最小的ROWID,然后删除不在其中的记录。 还需要考虑性能问题。如果表很大,使用窗口函数可能效率更高,因为ROW_NUMBER()可以在一次查询中处理,而ROWID的方法可能需要多次扫描。同时,删除操作需要谨慎,确保事务处理正确,最好在测试环境先验证。 还有用户提到的引用内容中,引用[1]提到了使用临时表和ROW_NUMBER()的方法,而引用[3]提到外键关联可能影响性能。但在这个问题中,可能主要是处理单表的重复数据,外键的影响可能不大,但需要确认是否有相关约束,避免删除时触发级联操作。 总结下来,最佳实践应该是使用ROW_NUMBER()函数来标记需要保留的行,然后根据这个标记删除其他重复行。或者使用ROWID结合子查询的方式。两种方法都需要在WHERE子句中排除要保留的行。同时,注意创建索引来优化查询速度,尤其是在处理大数据量时。 另外,用户可能还需要考虑是否有其他唯一标识字段,或者是否需要根据多个字段组合来判断重复。例如,重复数据可能由多个列的组合确定,这时候PARTITION BY子句需要包含所有这些列。 最后,确保在执行删除操作前备份数据,或者使用闪回功能(如引用[2]提到的FLASHBACK TABLE)以防万一。这也是最佳实践的一部分,避免数据丢失。</think>在Oracle数据库删除重复数据保留一条记录的最佳实践可通过以下步骤实现: --- ### **1. 使用ROW_NUMBER()窗口函数(推荐方案)** 通过窗口函数为重复数据组生成序号,删除非首条记录: ```sql DELETE FROM your_table WHERE rowid IN ( SELECT rid FROM ( SELECT rowid AS rid, ROW_NUMBER() OVER ( PARTITION BY column1, column2, column3 -- 指定判定重复的列 ORDER BY create_time DESC -- 按时间保留最新记录 ) AS rn FROM your_table ) WHERE rn > 1 -- 保留第1条,删除其他 ); ``` **特点**: - 明确通过`PARTITION BY`定义重复判定逻辑[^1] - 用`ORDER BY`控制保留优先级(如保留最新记录) - 性能较高,适合大数据量场景 --- ### **2. 基于ROWID的自连接删除** 通过子查询定位重复数据的最小ROWID作为保留目标: ```sql DELETE FROM your_table t1 WHERE rowid > ( SELECT MIN(rowid) FROM your_table t2 WHERE t1.column1 = t2.column1 -- 匹配重复列 AND t1.column2 = t2.column2 ); ``` **特点**: - 依赖物理地址实现去重 - 简单直观,但需注意执行效率 --- ### **3. 使用临时表过渡** ```sql CREATE TABLE temp_table AS SELECT DISTINCT * FROM your_table; TRUNCATE TABLE your_table; INSERT INTO your_table SELECT * FROM temp_table; DROP TABLE temp_table; ``` **特点**: - 适合字段较少的小型表 - 需要双倍存储空间 --- ### **最佳实践建议** 1. **索引优化**:为判定重复的列创建复合索引 ```sql CREATE INDEX idx_dup_check ON your_table(column1, column2); ``` 2. **事务保护**:在正式执行前使用`SAVEPOINT`或测试环境验证 3. **闪回保护**:可提前启用闪回功能便于恢复[^2] 4. **批量提交**:大表删除时使用`COMMIT`分批提交 ```sql BEGIN FOR i IN 1..100 LOOP DELETE FROM your_table WHERE rowid IN (...) AND ROWNUM <= 10000; COMMIT; END LOOP; END; ``` ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Charles Yan

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值