用SQL语句删除重复记录的四种好方法

 

 

问题:如何把具有相同字段的纪录删除,只留下一条。

例如:表test里有id,name字段,如果有name相同的记录只留下一条,其余的删除。name的内容不定,相同的记录数不定。

SQL语句删除重复记录的四种方法:

方法1:

1、将重复的记录记入temp1:

select [标志字段id],count(*) into temp1 from [表名]
               
               
group by [标志字段id]
               
               
having count(*)>1
              
              

2、将不重复的记录记入temp1:

insert temp1
              
              
select [标志字段id],count(*) from [表名]
               
               
group by [标志字段id]
               
               
having count(*)=1
              
              

3、作一个包含所有不重复记录的表:

select * into temp2 from [表名]
               
               
where 标志字段id in(select 标志字段id from temp1)
               
               

4、删除重复表:delete [表名]

5、恢复表:

insert [表名]
               
               
select * from temp2
              
              

6、删除临时表:

drop table temp1
              
              
drop table temp2
              
              

方法2:

declare @max integer,@id integer
              
              
declare cur_rows cursor local for 
              
              
select id,count(*) from 表名 group by id having count(*) > 1
               
               
open cur_rows
              
              
fetch cur_rows into @id,@max
              
              
while @@fetch_status=0
              
              
begin
              
              
select @max = @max -1
              
              
set rowcount @max
              
              
delete from 表名 where id = @id
               
               
fetch cur_rows into @id,@max
              
              
end
              
              
close cur_rows
              
              
set rowcount 0
              
              

:set rowcount @max - 1 表示当前缓冲区只容纳@max-1条记录﹐如果有十条重复的﹐就刪除

10条,一定会留一条的。也可以写成delete from 表名。

方法3

create table a_dist(id int,name varchar(20))
              
              

            
            
             
              
            
            
insert into a_dist values(1,'abc')
              
              
insert into a_dist values(1,'abc')
              
              
insert into a_dist values(1,'abc')
              
              
insert into a_dist values(1,'abc')
              
              

            
            
             
              
            
            
exec up_distinct 'a_dist','id'
              
              

            
            
             
              
            
            
select * from a_dist
              
              

            
            
             
              
            
            
create procedure up_distinct(@t_name varchar(30)
              
              
,@f_key varchar(30))
              
              
--f_key表示是分组字段﹐即主键字段
               
               
as
              
              
begin
              
              
declare @max integer,@id varchar(30) ,
              
              
@sql varchar(7999) ,@type integer
              
              
select @sql = 'declare cur_rows cursor 
              
              
for select '+@f_key+' ,count(*) from ' 
              
              
+@t_name +' group by ' +@f_key +' having count(*) > 1'
              
              
exec(@sql)
              
              
open cur_rows 
              
              
fetch cur_rows into @id,@max 
              
              
while @@fetch_status=0 
              
              
begin 
              
              
select @max = @max -1 
              
              
set rowcount @max 
              
              
select @type = xtype from syscolumns 
              
              
where id=object_id(@t_name) and name=@f_key
              
              
if @type=56
              
              
select @sql = 'delete from '+@t_name+' 
              
              
where ' + @f_key+' = '+ @id 
              
              
if @type=167
              
              
select @sql = 'delete from '+@t_name+' 
              
              
where ' + @f_key+' = '+''''+ @id +'''' 
              
              
exec(@sql)
              
              
fetch cur_rows into @id,@max 
              
              
end 
              
              
close cur_rows 
              
              
deallocate cur_rows
              
              
set rowcount 0
              
              
end
              
              

            
            
             
              
            
            
select * from systypes
              
              
select * from syscolumns where 
              
              
id = object_id('a_dist')
              
              

方法4

可以用IGNORE_DUP_KEY

create table dup (id int identity not null,
              
              
name varchar(50)not null)
              
              
go
              
              
insert into dup(name) values ('abc')
              
              
insert into dup(name) values ('abc')
              
              
insert into dup(name) values ('abc')
              
              
insert into dup(name) values ('abc')
              
              
insert into dup(name) values ('abc')
              
              
insert into dup(name) values ('abc')
              
              
insert into dup(name) values ('abc')
              
              
insert into dup(name) values ('cdefg')
              
              
insert into dup(name) values ('xyz')
              
              
insert into dup(name) values ('xyz')
              
              
go
              
              
select *
              
              
from dup
              
              
go
              
              
create table tempdb..wk(id int not null, 
              
              
name varchar(50)not null)
              
              
go
              
              
create unique index idx_remove_dup 
              
              
on tempdb..wk(name)
              
              
with IGNORE_DUP_KEY 
              
              
go
              
              
INSERT INTO tempdb..wk (id, name)
              
              
select id, name
              
              
from dup
              
              
go
              
              
select *
              
              
from tempdb..wk
              
              
go
              
              
delete from dup
              
              
go
              
              
set identity_insert dup on
              
              

            
            
             
              
            
            
INSERT INTO dup (id, name)
              
              
select id, name
              
              
from tempdb..wk
              
              
go
              
              
set identity_insert dup off
              
              
go
              
              
select *
              
              
from dup
              
              

            
            
             
              
            
            
go
              
              

注释:此处delete原表,再加入不重复的值。大家也可以通过joindelete原表中重复的值。

不要在SQL Server中盲目地追求一句处理 1

 

 

 

发布时间:2007.07.31 05:10     来源:赛迪网    作者:shuijing

 

在日常的学习和工作中,我们可以经常发现在处理SQL Server的时,很多人都会有一句出结果的习惯,但值得注意的是,不恰当的合并处理语句,往往会产生负面的性能,本篇针对使用 UNION ALL 代替 IF 语句的合并处理做出一个简单的事例,用来说明这种方法会所带来的负面结果。

示例:

A和表B,这两个表结构一致,为不同的业务服务,现在写一个存储过程,存储过程接受一个参数,当参数为0时,查询表A,参数为1时,查询表B

1:一般处理方法:

IF @Flag = 0
              
              

            
            
             
              
            
            
    SELECT * FROM dbo.A
              
              

            
            
             
              
            
            
ELSE IF @Flag = 1
              
              

            
            
             
              
            
            
    SELECT * FROM dbo.B
              
              

2、一句处理方法:

SELECT * FROM dbo.A
              
              

            
            
             
              
            
            
WHERE @Flag = 0
              
              

            
            
             
              
            
            
UNION ALL
              
              

            
            
             
              
            
            
SELECT * FROM dbo.B
              
              

            
            
             
              
            
            
WHERE @Flag = 1
              
              

细化分析:

从语句的简捷性来看,方法b具有技巧性,它们两者之间,究竟那一个更好呢?你可能会从性能上来评估,以决定到底用那一种。单纯从语句上来看,似乎两者的效率差不多,下面通过数据测试来反映结果似乎和想像的一样。

建立测试环境:(注,此测试环境主要是为几个主题服务,因此结构看起来稍有差异)

USE tempdb
              
              

            
            
             
              
            
            
GO
              
              

            
            
             
              
            
            
 
              
              

            
            
             
              
            
            
SET NOCOUNT ON
              
              

            
            
             
              
            
            
--======================================
              
              

            
            
             
              
            
            
--创建测试环境
               
               

            
            
             
              
            
            
--======================================
              
              

            
            
             
              
            
            
RAISERROR('创建测试环境', 10, 1) WITH NOWAIT
               
               

            
            
             
              
            
            
-- Table A
              
              

            
            
             
              
            
            
CREATE TABLE [dbo].A(
              
              

            
            
             
              
            
            
    [TranNumber] [int] IDENTITY(1, 1) NOT NULL,
              
              

            
            
             
              
            
            
    [INVNO] [char](8) NOT NULL,
              
              

            
            
             
              
            
            
    [ITEM] [char](15) NULL DEFAULT (''),
              
              

            
            
             
              
            
            
    PRIMARY KEY([TranNumber])
              
              

            
            
             
              
            
            
)
              
              

            
            
             
              
            
            
 
              
              

            
            
             
              
            
            
CREATE INDEX [indexONinvno] ON [dbo].A([INVNO])
              
              

            
            
             
              
            
            
CREATE INDEX [indexOnitem] ON [dbo].A ([ITEM])
              
              

            
            
             
              
            
            
CREATE INDEX [indexONiteminnvo] ON [dbo].A([INVNO], [ITEM])
              
              

            
            
             
              
            
            
GO
              
              

            
            
             
              
            
            
 
              
              

            
            
             
              
            
            
-- Table B
              
              

            
            
             
              
            
            
CREATE TABLE [dbo].B(
              
              

            
            
             
              
            
            
    [ItemNumber] [char](15) NOT NULL DEFAULT (''),
              
              

            
            
             
              
            
            
    [CompanyCode] [char] (4) NOT NULL,
              
              

            
            
             
              
            
            
    [OwnerCompanyCode] [char](4) NULL,
              
              

            
            
             
              
            
            
    PRIMARY KEY([ItemNumber], [CompanyCode])
              
              

            
            
             
              
            
            
)
              
              

            
            
             
              
            
            
 
              
              

            
            
             
              
            
            
CREATE INDEX [ItemNumber] ON [dbo].B([ItemNumber])
              
              

            
            
             
              
            
            
CREATE INDEX [CompanyCode] ON [dbo].B([CompanyCode])
              
              

            
            
             
              
            
            
CREATE INDEX [OwnerCompanyCode] ON [dbo].B([OwnerCompanyCode])
              
              

            
            
             
              
            
            
GO
              
              

            
            
             
              
            
            
 
              
              

            
            
             
              
            
            
--======================================
              
              

            
            
             
              
            
            
--生成测试数据
               
               

            
            
             
              
            
            
--======================================
              
              

            
            
             
              
            
            
RAISERROR('生成测试数据', 10, 1) WITH NOWAIT
               
               

            
            
             
              
            
            
INSERT [dbo].A([INVNO], [ITEM])
              
              

            
            
             
              
            
            
SELECT LEFT(NEWID(), 8), RIGHT(NEWID(), 15)
              
              

            
            
             
              
            
            
FROM syscolumns A, syscolumns B
              
              

            
            
             
              
            
            
 
              
              

            
            
             
              
            
            
INSERT [dbo].B([ItemNumber], [CompanyCode], [OwnerCompanyCode])
              
              

            
            
             
              
            
            
SELECT RIGHT(NEWID(), 15), LEFT(NEWID(), 4), LEFT(NEWID(), 4)
              
              

            
            
             
              
            
            
FROM syscolumns A, syscolumns B
              
              

            
            
             
              
            
            
GO
              
              

进行性能测试:

DECLARE @a int

 

SET @a = 1

 

 

 

DECLARE @t TABLE(

 

    id int IDENTITY,

 

    a int, b int)

 

DECLARE @dt datetime, @loop int, @id int

 

SET @loop = 0

 

WHILE @loop < 5

 

BEGIN

 

    SET @loop = @loop + 1

 

    RAISERROR('test %d', 10, 1, @loop) WITH NOWAIT

 

    SET @dt = GETDATE()

 

        SELECT [ITEM] FROM A

 

        WHERE @a = 0

 

            AND [ITEM] < 'A'

 

        UNION ALL

 

        SELECT [ItemNumber] FROM B

 

        WHERE @a = 1

 

            AND [ItemNumber] < 'A'

 

    INSERT @t(a) VALUES(DATEDIFF(ms, @dt, GETDATE()))

 

    SELECT @id = SCOPE_IDENTITY(), @dt = GETDATE()

 

        IF @a = 0

 

            SELECT [ITEM] FROM A

 

            WHERE [ITEM] < 'A'

 

        ELSE IF @a = 1

 

            SELECT [ItemNumber] FROM B

 

            WHERE [ItemNumber] < 'A'

 

    UPDATE @t SET b = DATEDIFF(ms, @dt, GETDATE())

 

    WHERE id = @id

 

END

 

SELECT * FROM @t

 

UNION ALL

 

SELECT NULL, SUM(a), SUM(b) FROM @t

性能测试结果:

id  a       b

 

--- ------- -------

 

1   3410   2063

 

2   1703   1656

 

3   1763   1656

 

4   1800   1793

 

5   1643   1856

 

NULL   10319  9024

从结果看,两者的性能差异很小,所以两者从性能上比较,可以视为没有差异。

问题所在:

虽然在性能上,两者没有什么差异,但另一个问题也许你从来没有考虑过,那就是对表的访问的问题,在方法A中,肯定只会访问到一个表;而在方法B中,情况还是如此吗?答案是否定的,方法B始终会扫描两个表。而这样的潜台词是,即使在我的查询中,只会用到A表,但如果B表被下了锁的话,整个查询就会被阻塞,而方法A不会。

为了证明这个问题,我们再做下面的测试

BLOCK 的测试为表A加锁: (查询窗口A)

BEGIN TRAN

 

    UPDATE A SET [ITEM] = RIGHT(NEWID(), 4)

 

    WHERE [ITEM] BETWEEN '9' AND 'A'

 

--ROLLBACK TRAN  -- 不回滚事务,让锁一直保持

BLOCK 的测试测试查询方法A(查询窗口B)

-- run query windows 2

 

DECLARE @a int

 

SET @a = 1

IF @a = 0

 

    SELECT [TranNumber] FROM A

 

    WHERE [ITEM] < 'A'

 

ELSE IF @a = 1

 

    SELECT [ItemNumber] FROM B

 

    WHERE [ItemNumber] < 'A'

BLOCK 的测试测试查询方法B(查询窗口C)

-- run query windows 3

 

DECLARE @a int

 

SET @a = 1

 

 

 

SELECT [ITEM] FROM A

 

WHERE @a = 0

 

    AND [ITEM] < 'A'

 

UNION ALL

 

SELECT [ItemNumber] FROM B

 

WHERE @a = 1

 

    AND [ItemNumber] < 'A'

结果:

可以看到,查询窗口B中的查询会及时地完成,而查询窗口C的查询会一直等待,你可以通过执行存储过程 sp_who2,查看当前的BLOCK状况来确定查询窗口C的查询是否被查询窗口A的查询BLOCK住。

最后结论:

不要使用查询方法B,它看起来很不错,实际的结果即则是会增加被BLOCK的机会。

用人工智能技术自动对SQL语句进行重写 1

 

 

 

发布时间:2008.04.11 04:56     来源:赛迪网    作者: Lynn

 

【赛迪网-IT技术报道】SQL语句的优化是将性能低下的SQL语句转换成目的相同的性能优异的SQL语句。

人工智能自动SQL优化就是使用人工智能技术,自动对SQL语句进行重写,从而找到性能最好的等效SQL语句。

数据库性能的优化

一个数据库系统的生命周期可以分成:设计、开发和成品三个阶段。在设计阶段进行数据库性能优化的成本最低,收益最大。在成品阶段进行数据库性能优化的成本最高,收益最小。

数据库的优化通常可以通过对网络、硬件、操作系统、数据库参数和应用程序的优化来进行。最常见的优化手段就是对硬件的升级。根据统计,对网络、硬件、操作系统、数据库参数进行优化所获得的性能提升,全部加起来只占数据库系统性能提升的40%左右,其余的60%系统性能提升来自对应用程序的优化。许多优化专家认为,对应用程序的优化可以得到80%的系统性能的提升。

应用程序的优化

应用程序的优化通常可分为两个方面:源代码和SQL语句。由于涉及到对程序逻辑的改变,源代码的优化在时间成本和风险上代价很高,而对数据库系统性能的提升收效有限。

为什么要优化SQL语句

. SQL语句是对数据库进行操作的惟一途径,对数据库系统的性能起着决定性的作用。

. SQL语句消耗了70%90%的数据库资源。

. SQL语句独立于程序设计逻辑,对SQL语句进行优化不会影响程序逻辑。

. SQL语句有不同的写法,在性能上的差异非常大。

. SQL语句易学,但难精通。

优化SQL语句的传统方法是通过手工重写来对SQL语句进行优化。DBA或资深程序员通过对SQL语句执行计划的分析,依靠经验,尝试重写SQL语句,然后对结果和性能进行比较,以试图找到性能较佳的SQL语句。这种传统上的作法无法找出SQL语句的所有可能写法,且依赖于人的经验,非常耗费时间。

SQL优化技术的发展历程

第一代SQL优化工具是执行计划分析工具。这类工具针对输入的SQL语句,从数据库提取执行计划,并解释执行计划中关键字的含义。

第二代SQL优化工具只能提供增加索引的建议,它通过对输入的SQL语句的执行计划的分析,来产生是否要增加索引的建议。

第三代SQL优化工具不仅分析输入SQL语句的执行计划,还对输入的SQL语句本身进行语法分析,经过分析产生写法上的改进建议。

人工智能自动SQL优化

人工智能自动SQL优化出现在90年代末。目前在商用数据库领域,LECCO Technology Limited(灵高科研有限公司)拥有该技术,并提供使用该技术的自动优化产品LECCO SQL Expert,它支持OracleSybaseMS SQL ServerIBM DB2数据库平台。该产品针对数据库应用的开发和维护阶段提供的模块有:SQL语法优化器、PL/SQL集成化开发调试环境(IDE)、扫描器、数据库监视器等。其核心模块SQL 语法优化器的工作原理为:输入一条源SQL语句;人工智能反馈式搜索引擎对输入的SQL语句,结合检测到的数据库结构和索引进行重写,产生N条等效的SQL语句输出;产生的N条等效SQL语句再送入人工智能反馈式搜索引擎进行重写,直至无法产生新的输出或搜索限额满;对输出的SQL语句进行过滤,选出具有不同执行计划的SQL语句;对得到的SQL语句进行批量测试,找出性能最好的SQL语句。

LECCO SQL Expert自动优化实例

假设我们从源代码中抽取出这条SQL语句(也可以通过内带的扫描器或监视器获得SQL语句)

SELECT COUNT(*)

FROM EMPLOYEE

swheresEXISTS (SELECT 'X'

FROM DEPARTMENT

swheresEMP_DEPT=DPT_ID

AND DPT_NAME LIKE 'AC%')

AND EMP_ID IN (SELECT SAL_EMP_ID

FROM EMP_SAL_HIST B

swheresSAL_SALARY 70000)

按下优化按钮后,经过10几秒,SQL Expert就完成了优化的过程,并在这10几秒的时间里重写产生了2267 条等价的SQL语句,其中136SQL语句有不同的执行计划。

接下来,我们可以对自动重写产生的136SQL语句进行批运行测试,以选出性能最佳的等效SQL语句。按下批运行按钮,在终止条件页选择最佳运行时间SQL语句,按确定

经过几分钟的测试运行后,我们可以发现SQL124的运行时间和反应时间最短。运行速度约有22.75倍的提升(SQL语句运行时间为2.73秒,SQL124运行时间为0.12)。现在我们就可以把SQL124放入源代码中,结束一条SQL语句的优化工作了。

边做边学式训练提升SQL开发水平

LECCO SQL Expert不仅能够找到最佳的SQL语句,它所提供的边做边学式训练还能够教开发人员和数据库管理员如何写出性能最好的SQL语句。LECCO SQL Expert“SQL比较器可以标明源SQL和待选SQL间的不同之处。

以上面优化的结果为例,为了查看源SQL语句和SQL124在写法上有什么不同,我们可以按下比较器按钮,对SQL124和源SQL语句进行比较。“SQL 比较器SQL124相对于源SQL语句的不同之处以蓝颜色表示了出来。如果选择双向比较复选框,“SQL 比较器可以将两条SQL语句的不同之处以蓝色表示。当然,我们也可以从源语句和重写后的SQL 语句中任选两条进行比较。

从比较的结果可以看到,重写后的SQL124把第一个Exists改写成了In;在字段DPT_ID上进行了合并空字符串的操作,以诱导数据库先执行子查询中的

(SELECT DPT_ID||''

FROM DEPARTMENT

WHERE DPT_NAME LIKE 'AC%')

在子查询完成后,再与EMPLOYEE表进行嵌套循环连接(Nested Loop Join)

如果觉得对写法的改变难以理解,还可以点中执行计划复选框,通过比较两条SQL语句的执行计划的不同,来了解其中的差异。在查看执行计划过程中,如果有什么不明白的地方,可以点中“SQL信息按钮,再点击执行计划看不明白的地方,LECCO SQL Expert的上下文敏感帮助系统将提供执行计划该处的解释。

“SQL比较器中,选中统计信息复选框后,可得到详细的两条SQL语句运行时的统计信息比较,这对于学习不同的SQL写法对数据库资源的消耗很有帮助。

LECCO SQL Expert优化模块的特点

LECCO SQL Expert优化模块的特点主要表现为:自动优化SQL语句;以独家的人工智能知识库反馈式搜索引擎来重写性能优异的SQL语句;找出所有等效的SQL语句及可能的执行计划;保证产生相同的结果;先进的SQL语法分析器能处理最复杂的SQL语句;可以重写SELECTSELECT INTOUPDATEINSERTDELETE语句;通过测试运行,为应用程序和数据库自动找到性能最好的SQL语句;提供微秒级的计时,能够优化Web应用程序和有大量用户的在线事务处理中运行时间很短的SQL语句;为开发人员提供边做边学式训练,迅速提高开发人员的SQL编程技能;提供上下文敏感的执行计划帮助系统和SQL运行状态帮助;不是猜测或建议,而是独一无二的SQL重写解决方案。

写出专家级的SQL语句

LECCO SQL Expert的出现,使SQL的优化变得极其简单,只要能够写出SQL语句,它就能帮用户找到最好性能的写法。LECCO SQL Expert不仅能在很短的时间内找到所有可能的优化方案,而且能够通过实际测试,确定最有效的优化方案。同以往的数据库优化手段相比较,LECCO SQL Expert将数据库优化技术带到了一个崭新的技术高度,依赖人的经验、耗费大量时间、受人的思维束缚的数据库优化手段已经被高效、省时且准确的自动优化软件所取代了。通过内建的“LECCO小助手的帮助,即使是SQL的开发新手,也能快速且简单地写出专家级的SQL语句。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值