扫一扫
分享文章到微信
扫一扫
关注官方公众号
至顶头条
作者:csdn 来源:csdn 2009年12月18日
关键字: MS-SQL Server 问答
数据修改之插入数据,更新数据,删除数据
SQL code这篇说的是数据修改,这里无非是三种DML操作:插入数据,更新数据,删除数据.
1.插入数据:
a.select into
--》select * into newtable from oldtable
它的作用是从一个表oldtable 选取数据插入到一个新表newtable,但是没有返回值,只是建了个有数据的新表.
新表有原来表oldtable 的选出来的结果集的列属性,包括IDENTITY属性.但是不会从oldtable复制约束,索引,触发器等。
这里讲相关的2个小技巧:
1.复制表结构
select * into newtable from oldtable where 1=2
因为1永远不等于2 所以不会有任何数据从原表进入新表,但是结构确实实实在在复制过去了。
2.改变复制列属性
前面提过了,复制表结构过去的时候,列的IDENTITY属性会跟着过去,如果你想复制过去的时候不要这个属性,可以这样:
create table #(id int identity(1,1),a int)
insert # select 1
insert # select 2
insert # select 3
insert # select 7
insert # select 9
select ID+0 as id ,A into #1 from #
insert #1(a) select 8
select * from #1
/*
id A
----------- -----------
NULL 8
1 1
2 2
3 3
4 7
5 9
*/
------插入8 没有自增
b.insert exec
这条语句可以把存储过程或者动态处理的结果集插入保存到目标表里。
举个小例子:
---存储过程
Create procedure up_test1 as
select '1111'
go
create table #tt (a varchar(4))
insert into #tt exec up_test1
select * from #tt
/*
-------
1111
*/
---动态
create table #pp (a int)
declare @s varchar(100)
set @s='declare @n int set @n=1 select @N'
insert #pp
exec (@s)
select * from #pp
/*
a
-----------
1
*/
这里还有个INSERT EXEC PROC不能嵌套的问题.
解决方法:http://blog.csdn.net/DJ2008/archive/2008/12/22/3583645.aspx
c.带有OUTPUT的INSERT
SQL2005中不仅INSERT支持OUTPUT update delete也支持它 利用它可以返回输出。它主要是通过INSERTED 和DELETED表进行操作.(它和触发器里的2个表保存的东西性质是一样)
这里演示个简单例子:
问题描述---》》我想知道最新插入的列的标识值
create table #k(id int identity (1,1) ,val int )
insert #k select 1
insert #k select 6
insert #k select 8
--单列
insert #k select 9
select SCOPE_IDENTITY()
/*
---------------------------------------
4
*/
--多列
insert #k select 3
union all select 4
union all select 6
select SCOPE_IDENTITY() --这里只能返回一个7 也就是最新的一个序号 如果想要 5 6 7 可以利用output
----output 解决方案
declare @V TABLE (ID INT)
insert #k
OUTPUT inserted.id INTO @V
--OUTPUT inserted.id 这里去掉注释 可以返回输出给调用方
select 3
union all select 4
union all select 6
select * from @V
/*
ID
-----------
5
6
7
*/
2.删除数据
A.truncate 和 delete
当你想清空表的数据的时候,有2个选择--truncate 和 delete。如果数据就是被清理而且数据又不需要恢复的时候,强烈介意你使用TRUNCATE.
原因很简单,它总是以最小的方式记录到日志,不像DELETE是完整的记录到日志中,数据量越大 你就越能体验这2者巨大的速度差。
PS:1.turncate操作不会触发表上的DELETE操作触发器
2.如果有外键指向目标表,它将拒绝TRUNCATE操作,即使外表为NULL或者外键被禁用.(这里你就可以建个虚构表,并指向一个重要表,防止重要表数据被意外执行TURNCATE 和DROP )
--重要的表
create table im_t(a Int primary key )
insert im_t select 100 union all select 200
--外表-保护表
create table b_hu(a int,b int)
alter table b_hu add constraint FK_a foreign key (a) references im_t(a)
--进行TRUNCATE 操作
truncate table im_t
/*
消息 4712,级别 16,状态 1,第 1 行
无法截断表 'im_t',因为该表正由 FOREIGN KEY 约束引用。
*/
3.TRUNCATE操作会把IDENTITY的属性从新从种子开始,delete不会
B.移除重复的行
其实已经有很多人总结过,我就再随便写几个吧.
a.整行都重复
create table test1(a int ,b int)
insert test1 select 1,2
union all select 1,2
union all select 1,2
union all select 1,3
union all select 1,4
union all select 2,2
union all select 2,2
union all select 2,6
union all select 2,7
--方法:移除重复行(整行相同的),转移-》删除原表->更改表名
select distinct * into po from test1
drop table test1
exec sp_rename 'po','test1'
select * from test1
/*
a b
----------- -----------
1 2
1 3
1 4
2 2
2 6
2 7
*/
b.在上面新的test1表里面移除部分重复
--1.以a分组,取最大的B
delete a
from test1 a
where B<>(select MAX(b) from test1 where a.a=a)
--or
delete a
from test1 a
where B<>(select top 1 b from test1 where a.a=a order by B desc )
--or
delete a
from test1 a
where exists(select * from test1 where a.a=A and b>a.b)
--or
with cte as
(
select ROW_NUMBER() over(partition by a order by b desc ) as rn,*
from test1
)
delete cte where rn<>1
select * from test1
/*
a b
----------- -----------
1 4
2 7
*/
--2 如果你想最大的2组
delete a
from test1 a
where B not in(select top 2 b from test1 where a.a=a order by B desc )
--or
with cte as
(
select ROW_NUMBER() over(partition by a order by b desc ) as rn,*
from test1
)
delete cte where rn<>1 or rn<>2
----------------------
select * from test1
/*
a b
----------- -----------
1 3
1 4
2 6
2 7
/*
C.带有OUTPUT的delete
这里我就说一个情况,个人觉得比较合适.如果你要从一个表转移部分数据到另外一个表里去.
你可以先转移然后在原表里删除那些数据.使用这个方法,你必须使用锁锁定要转移的行,否则你不能保证增加一些符合转移条件的
新行插入到表里在你转移的时候。如果使用OUTPUT这2步就可以解决这个问题,而且很方便.
create table m_s(a int )
create table y_s(a int )
insert y_s
select 1 union all
select 3 union all
select 4 union all
select 5 union all
select 6 union all
select 7
--Y_S表删除6以下的记录到m_s表
delete y_s
output deleted.a into m_s(a)
where a<6
select * from m_s
/*
a
-----------
1
3
4
5
*/
PS:如果这个时候M_S表上的触发器或者CHECK约束,外键等等不会阻止数据的插入.如果你想过滤,可以先插入到一个临时表,然后再转移到M_S表
3.更新数据
a.基于连接的UPDATE
这没什么好说的,大家平时都在用。写个SQL2005的CTE更新
create table #old (id int,oval int)
create table #new (id int,nval int)
insert #old
select 1,1 union all
select 2,2 union all
select 3,1 union all
select 4,5 union all
select 5,7
insert #new
select 1,2 union all
select 2,3 union all
select 3,4 union all
select 4,8 union all
select 5,9
;with cte as
(
select old=a.oval,NEw=b.nval
from #old a join #new b on a.id=b.id
)
update cte
set old=NEw
select * from #old
/*
id oval
----------- -----------
1 2
2 3
3 4
4 8
5 9
*/
b.赋值UPDATE
IF OBJECT_ID('dbo.T1') IS NOT NULL
DROP TABLE dbo.T1;
GO
CREATE TABLE dbo.T1
(
col1 INT NOT NULL,
col2 VARCHAR(5) NOT NULL
);
INSERT INTO dbo.T1(col1, col2) VALUES(0, 'A');
INSERT INTO dbo.T1(col1, col2) VALUES(0, 'B');
INSERT INTO dbo.T1(col1, col2) VALUES(0, 'C');
INSERT INTO dbo.T1(col1, col2) VALUES(0, 'C');
INSERT INTO dbo.T1(col1, col2) VALUES(0, 'C');
INSERT INTO dbo.T1(col1, col2) VALUES(0, 'B');
INSERT INTO dbo.T1(col1, col2) VALUES(0, 'A');
INSERT INTO dbo.T1(col1, col2) VALUES(0, 'A');
INSERT INTO dbo.T1(col1, col2) VALUES(0, 'C');
INSERT INTO dbo.T1(col1, col2) VALUES(0, 'C');
--2000变量 只负责加序号 不需要排序
DECLARE @i AS INT;
SET @i = 0;
UPDATE dbo.T1 SET @i = col1 = @i + 1;--等价于SET @i =@i+1,col1=@i
/*
/*
col1 col2
----------- -----
1 A
2 B
3 C
4 C
5 C
6 B
7 A
8 A
9 C
10 C
*/
*/
--2005CTE 有顺序地加序号
WITH T1RN AS
(
SELECT col1, ROW_NUMBER() OVER(ORDER BY col2) AS RowNum
FROM dbo.T1
)
UPDATE T1RN SET col1 = RowNum;
-------------
select * from T1 order by col1
/*
col1 col2
----------- -----
1 A
2 A
3 A
4 B
5 B
6 C
7 C
8 C
9 C
10 C
*/
备注:
数据的修改与日志记录量相关.当设计插入程序的时候,注意2个东西--一个事日志记录量,还有一个事事务大小.
1。日志记录量
SQL 一般总是完整 的记录修改操作,但是对数据库恢复模式不是FULL而且执行的是BULK操作的时候,它总是以最小的方式记录.
BULK操作包括:创建和重新生产索引,使用BULK引起插入,select into ,LOB(大型对象)的操作.
设计插入时候:先考虑BULK操作;再考虑基于集合的多行INSERT(insert select );最后才选择单行的INSERT 。
2。事务大小
一行插入一个事务会向事务日志里写东西.应该尽量把多行处理封装到单个事务.
------T-MAC
http://blog.csdn.net/feixianxxx/archive/2009/10/31/4751769.aspxmy blog
如果您非常迫切的想了解IT领域最新产品与技术信息,那么订阅至顶网技术邮件将是您的最佳途径之一。
现场直击|2021世界人工智能大会
直击5G创新地带,就在2021MWC上海
5G已至 转型当时——服务提供商如何把握转型的绝佳时机
寻找自己的Flag
华为开发者大会2020(Cloud)- 科技行者