您的位置:首页 > 数据库

【Transact-SQL】一句SQL删除重复记录

2011-08-14 13:57 169 查看
星期五去一家公司面试SQL数据库工程师,有一份笔试题目,其中有一题是:现在有一个表t(姓名,手机号,联系方式),手机号字段存在重复记录,要求用一句SQL删除重复记录。
我想了想写了一句创建唯一索引的语句:
create unique index t_index on t(手机号)
交卷了,后来想想也不对,这样只能保证以后不能添加重复的记录,这样写会导致:当添加了重复记录时,发生错误,导致整个添加操作被回滚。实际上应该写成这样:
create unique index t_index on t(手机号) with ignore_dup_key
这样才能保证当添加重复记录时候,忽略重复记录,如果有2条记录是重复的,那么只会添加其中的一条,而且不会在执行的时候发生错误。

面试人员看了我做的题,说让我把这一题再重新做做,然后再给他看,我就重新想了2种写法。我按照第一种写法写得语句,面试人员说这种办法一看就是错误的。
第二种写法,面试人员看说:基本是对的,就是效率差点。呵呵,他说的很对,效率确实差,

下面把两种办法都写出来,测试环境SQL Server 2005:
首先建表:

create table t (ename varchar(10),mobile char(5),relation varchar(5))

insert into t values('abcd','12345','cc') --和第3个重复
insert into t values('ab', '12358','dd') --和第5个重复
insert into t values('abc', '12345','gg')
insert into t values('abde','24685','hh')
insert into t values('abdf', '12358','ag')
insert into t values('a', '54321','op')

select * into tt from t --以下的操作都在tt上进行

1.先说第二种写法,就是通过子查询,查到count大于1的记录,不过这个语句经过测试,是错误的。
delete tt from tt t1
where
(select count(mobile) from tt t2 where t2.mobile = t1.mobile)>1

结果:(4 行受影响)
一下子删除了4条记录,也就是只剩下第4和第6条记录,为什么会这样呢?主要是子查询没有变化,虽然tt一开始会删除一条重复的记录,但是子查询的表没变化,所以导致凡是tt表中count(mobile)>1的,也就是有重复的记录,整个都删除了。

2.第二种写法,虽然语法有点问题,但是回家后,经过测试,应该说基本的想法是对的,就是查了一下资料,把语法改了一下。下面的语句适合mobile字段重复的情况,如果还有其它字段也重复,稍加修改就行。

delete t from t t1 inner join
(
select *,row_number() over(partition by mobile order by mobile) as row from t
) t2
on t1.ename = t2.ename and t1.mobile = t2.mobile and t1.relation = t2.relation
where t2.row >1

SQL Server 分析和编译时间:
CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。

SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。
SQL Server 分析和编译时间:
CPU 时间 = 47 毫秒,占用时间 = 53 毫秒。
表 'tt'。扫描计数 2,逻辑读取 6 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。
表 'Worktable'。扫描计数 2,逻辑读取 22 次,物理读取 0 次,预读 0 次,lob 逻辑读取 0 次,lob 物理读取 0 次,lob 预读 0 次。

(2 行受影响)

(1 行受影响)

SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 0 毫秒。
SQL Server 分析和编译时间:
CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。

SQL Server 执行时间:
CPU 时间 = 0 毫秒,占用时间 = 1 毫秒。

从效率上看,对数据缓冲的读取次数比较多,特别是Worktble的读取次数特别高,毕竟表中只有6条记录。

呵呵,面试人员后来问我,为什么我做的几道题里都用row_number,我没回答,其实是最近正好看到这个函数,确实改进了sql server 2000中显示行号的效率,挺有用的。

另一种更为简单的删除重复记录的写法:

drop table rt  create table rt(a varchar(10),b varchar(10))  insert into rt select 'a1',   'b1' union all select 'a1',  'b1'  union all select 'a1', 'b2'   ;with t as ( select *,        row_number() over(partition by a,b order by a) rownum from rt )   delete from t where rownum = 2   select * from rt /* a	b a1	b1 a1	b2 */   /*  ;with t as ( select *,        row_number() over(partition by a,b order by a) rownum from rt )  --很有意思的时候,这种写法会把所有的3条数据都删除掉,所以这种写法是有问题的 delete from t  from t t2 inner join t t1         on t1.a = t2.a            and t1.b = t2.b            and t1.rownum = t2.rownum            and t1.rownum = 2 */


本文出自 “探索SQLServer” 博客,请务必保留此出处http://yupeigu.blog.51cto.com/3323001/1368145
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: