三種DML操作:插入數據,更新數據,刪除數據.

這篇說的是數據修改,這裏無非是三種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.
truncatedelete
當你想清空表的數據的時候,有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。事務大小
一行插入一個事務會向事務日誌裏寫東西.應該儘量把多行處理封裝到單個事務.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章