oracle中按指定字段排序删除重复记录

本文介绍了Oracle数据库中两种有效的排重方法:使用rowid和row_number()函数。通过具体实例展示了如何利用这两种方法来删除重复记录,同时保留所需的标准记录。

       今天看了一下项目上的DBA针对某一模块中的业务写的存储过程,里面数据清洗的过程中时常会进行排重操作,自己总结了一下oracle中排重的思路

1,使用rowid来作为限制条件排重

rowidrownum的区别
两者都是伪列,rowid是物理结构上的,而rownum是逻辑结构上的,rowid是用于定位数据库中某条记录的相对唯一的地址

通常情况下数据在插入数据库中时就已经被确定且唯一,使用rowid可以定位该行的物理地址信息,rownum是动态的,查询   语句的不同,rownum是不同的,当查询语句为以empno升序时SMITH的rownum是1,而当以降序排列时,SMITH的    rownum就是14,由于rowid的唯一性,所以使用rowid来实现排重,即在根据条件查询后的结果上加上rowid<>1就行了

2,使用row_number() over(partition by column1 order by column2) 来实现排重

上面这句语句的意思是在以column1分组后并且组内以column2排序的条件下分级:

over:  在什么条件之上。
partition by e.deptno:  按部门编号划分(分区)。
order by e.sal desc:  按工资从高到低排序(使用rank()/dense_rank()/row_number() 时,必须要带order by否则非法);
rank()/dense_rank()/row_number() :  分级
整个语句的意思就是:
在按部门划分的基础上,按工资从高到低对雇员进行分级,“级别”由从小到大的数字表示(最小值一定为1)
row_number(),rank(),dense_rank()的区别:
在'分级时',如果有两个记录按照当前的筛选条件是相同的排名,那么,rank()会有两个分级为一 的记录,接下来为第三级,

跳跃排序,而dense_rank()则是有两个第一级后接下来还是第二级,即连续排序,row_number()则是第一级后会按照默认的规则排序后显示第二级

举例说明:

1,以部门编号分组,并且组内以工资降序排列后分级:

select e.deptno, e.empno,e.ename,e.sal,rank() over(partition by deptno order by sal desc) from emp e;

结果如下:

部门号为20 的部门中有两个工资为3500的员工,在分级时使用的是rank(),所以两个3500的分级都是1,接着是3

2,使用dense_rank() 分级

select e.deptno, e.empno,e.ename,e.sal,dense_rank() over(partition by deptno order by sal desc) from emp e;

结果如下:


两个工资为3500的员工的分级依旧是1,但是接下来的员工的分级是2

3,使用row_number()分级

select e.deptno, e.empno,e.ename,e.sal,row_number() over(partition by deptno order by sal desc) from emp e;

结果如下:


按照员工的工资来分级出现重复后,会按照默认的顺序来分级,但是不会出现相同的级别

我们就可以利用row_number()的这个特性来

delete from t_visit_customer p where p.rowid in (
select e.rowid from 
(select t.rowid,row_number() over(partition by t.credential_no order by t.is_gen_self desc nulls last) rw
from t_visit_customer t where t.branch_code = in_branch_code) e where e.rw<>1;
) and p.branch_code = in_branch_code;

可以分解为以下三个步骤:

-- 使用row_number()  over(partiton by ...)实现排重
-- 其中t_visit_customer为表名,in_branch_code为存储过程中的输入参数
-- 1,查找记录行的rowid和使用row_number() over(partition by...)来查找对应分组条件和排序条件下的顺序
select t.rowid,row_number() over(partition by t.credential_no order by t.is_gen_self desc nulls last) rw
from t_visit_customer t where t.branch_code = in_branch_code;
-- 2,查找重复的数据的rowid
select e.rowid from 
(select t.rowid,row_number() over(partition by t.credential_no order by t.is_gen_self desc nulls last) rw
from t_visit_customer t where t.branch_code = in_branch_code) e where e.rw<>1;
-- 3,删除原表中rowid为重复数据的rowid的列进行排重
delete from t_visit_customer p where p.rowid in (
select e.rowid from 
(select t.rowid,row_number() over(partition by t.credential_no order by t.is_gen_self desc nulls last) rw
from t_visit_customer t where t.branch_code = in_branch_code) e where e.rw<>1;
) and p.branch_code = in_branch_code;

即:去除公司代码为in_branch_code的公司中credential_no相同的记录并且留下的是is_gen_self最大的记录


小弟是菜鸟,如有错误,欢迎大佬们指教!

<think>我们面对的是一个Oracle数据库查询问题:用户希望查询表数据,并除特定字段重复记录。注意,用户要求的是除特定字段复,而不是整行复。这意味着,当指定字段组合复时,我们只保留其中一条记录(通常保留rowid最小的那条)。根据引用资料,我们可以使用以下方法:1.使用rowid:通过比较rowid删除或筛选重复记录(如引用[3]所示)。 2.使用子查询和分组:通过分组和聚合函数(如max(rowid))来找出需要保留的记录(如引用[2]和引用[3])。具体思路: -我们首先需要确定哪些字段组合不能复(即用户指定的特定字段)。-然后,我们通过分组这些字段,并找出每组中rowid最小(或最大)的那条记录,从而得到不复的记录。 参考引用[3]的删除语句,我们可以改写为查询语句。例如,如果我们想保留特定字段组合下rowid最小的记录,可以这样写: ```sqlSELECT *FROM表名aWHERE rowid =(SELECT MIN(rowid)FROM表名bWHEREa.字段1= b.字段1 ANDa.字段2= b.字段2 AND... --指定需要字段);``` 或者,另一种常见写法(使用窗口函数): ```sqlSELECT *FROM (SELECTt.*,ROW_NUMBER()OVER (PARTITIONBY字段1,字段2, ...ORDER BYrowid) rnFROM表名 t)WHERE rn =1; ```这里,我们使用`ROW_NUMBER()`函数,按照指定字段分组(PARTITION BY),并在组内按照rowid排序(也可以按照其他字段排序,比如时间字段,这里按rowid表示保留最早插入的记录),然后取每组的第一条。用户没有指定具体保留哪一条,通常我们保留rowid最小的(即最早插入的)。 因此,我们可以提供两种方法。注意:用户要求的是查询,所以我们可以直接给出查询语句。根据用户提供的引用,我们还可以参考引用[4]的写法,但引用[4]是查询重复记录,而我们需要的是后的结果。所以,我们给出以下解决方案: ###方法1:使用子查询和MIN(rowid)```sqlSELECT* FROM你的表名aWHERE rowid IN(SELECT MIN(rowid)FROM你的表名GROUPBY字段1,字段2, ...--指定字段);``` ###方法2:使用ROW_NUMBER()窗口函数(推荐,更灵活)```sqlSELECT* FROM(SELECT t.*,ROW_NUMBER() OVER(PARTITION BY字段1,字段2,... ORDERBY rowid)AS rnFROM你的表名t) WHERErn=1;``` ###说明: -将`你的表名`替换为实际的表名。 -将`字段1,字段2,...`替换为需要字段名(即指定这些字段组合不能复)。-在窗口函数中,`ORDER BY`子句可以调整,比如如果想保留最新插入的记录(假设rowid越大表示越新),可以改为`ORDER BYrowidDESC`,然后取`rn=1`。 ###示例: 假设我们有一个表`employee`,我们想根据`name`和`department`两个字段(即这两个字段组合相同的只保留一条),保留rowid最小的记录。 使用方法1:```sqlSELECT* FROMemployee aWHERErowidIN (SELECTMIN(rowid)FROMemployeeGROUPBY name, department);``` 使用方法2:```sqlSELECT* FROM(SELECT e.*,ROW_NUMBER() OVER(PARTITION BYname,department ORDERBY rowid)AS rnFROMemployee e)WHERE rn =1; ```这两种方法都可以实现需求。方法2更灵活,因为可以在排序指定其他字段(比如按时间字段倒序,保留最新的记录)。###注意事项:1.如果表数据量很大,使用窗口函数的方法可能会占用较多内存,但通常效率是可以接受的。2.确保指定字段是正确的,以免误删数据。 §§相关问题§§1.如何删除Oracle表中特定字段重复记录(只保留一条)?2.除了rowidOracle中还有哪些方法可以标识记录的唯一性? 3. Oracle中如何查找特定字段复的记录?</think>### Oracle查询表数据并除特定字段重复记录Oracle除特定字段重复记录(保留一条)的常用方法如下,核心思路是使用`ROWID`或窗口函数识别复行: #### 方法1:使用ROWID子查询(高效推荐) ```sql SELECT * FROM 表名 a WHERE ROWID = ( SELECT MIN(ROWID) FROM 表名 b WHERE a.字段1 = b.字段1 -- 替换为实际字段名 AND a.字段2 = b.字段2 -- 指定字段 -- 可继续添加其他字段条件 ); ``` **说明**: - 通过`MIN(ROWID)`保留每组重复记录中的第一条(按物理存储顺序)[^3] - 在`WHERE`子句中指定需要字段(如字段1、字段2等) #### 方法2:使用ROW_NUMBER()窗口函数(灵活排序) ```sql SELECT * FROM ( SELECT t.*, ROW_NUMBER() OVER ( PARTITION BY 字段1, 字段2 -- 指定字段 ORDER BY ROWID -- 可替换为其他排序字段 ) AS rn FROM 表名 t ) WHERE rn = 1; ``` **优势**: - 可通过`ORDER BY`控制保留哪条记录(如最新/最旧数据) - 支持复杂排序规则(如按时间戳倒序保留最新记录) #### 方法3:GROUP BY聚合(需明确字段) ```sql SELECT MAX(ROWID) AS rid, 字段1, 字段2 -- 必须包含所有字段 FROM 表名 GROUP BY 字段1, 字段2 -- 指定分组字段 ``` > **关键注意事项**: > 1. **字段选择**:在`WHERE`/`PARTITION BY`中需列出所有需字段 > 2. **保留规则**:默认保留`ROWID`最小的记录,可通过`ORDER BY`调整 > 3. **性能优化**:对字段创建索引可加速查询 > 4. **字段处理**:非字段需用聚合函数(如`MAX(其他字段)`)或通过子查询获取 ### 示例场景 假设表`employees`需除`name`+`department`复项: ```sql -- 保留每组复项中ROWID最小的记录 SELECT * FROM employees a WHERE ROWID = ( SELECT MIN(ROWID) FROM employees b WHERE a.name = b.name AND a.department = b.department ); ``` ### 验证重复记录 检查指定字段是否存在复: ```sql SELECT 字段1, 字段2, COUNT(*) FROM 表名 GROUP BY 字段1, 字段2 HAVING COUNT(*) > 1; ``` [^1]: Oracle通过ROWID物理地址标识记录唯一性,是操作的核心依据 [^3]: MIN(ROWID)可确保每组重复记录仅保留最早插入的一条 [^4]: 多字段需在条件中明确所有关联字段
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值