在INSERT、UPDATE、DELETE語句中使用OUTPUT得到語句影響的每行信息,今天我們來學習這個語法。
1、OUTPUT_CLAUSE定義(語法參Transact-SQL 語法約定):
<OUTPUT_CLAUSE> ::=
{
[ OUTPUT <dml_select_list> INTO { @table_variable | output_table } [ ( column_list ) ] ]
[ OUTPUT <dml_select_list> ]
}
<dml_select_list> ::=
{ <column_name> | scalar_expression } [ [AS] column_alias_identifier ]
[ ,...n ]
<column_name> ::=
{ DELETED | INSERTED | from_table_name } . { * | column_name }
2、OUTPUT_CLAUSE說明:
返回受 INSERT、UPDATE 或 DELETE 語句影響的每行的信息,或者返回基於上述每行的表達式。這些結果可以返回到處理應用程序,以供在確認消息、存檔以及其他類似的應用程序要求中使用。此外,也可以將結果插入表或表變量。
3、典型應用:
1、根據當前表的數據有條件的生成歷史或新的初始化數據;
2、把INSERT、UPDATE 或 DELETE 語句影響的每行的信息暫存處理或反饋給應用程序完成業務或邏輯的完整性;
3、OUTPUT 子句對於在 INSERT 或 UPDATE 操作之後檢索標識列或計算列的值可能非常有用;
4、示列:
1、根據當前表的數據有條件的生成歷史數據;
記得我以前做零售及水廠應用系統時都會有一個月未數據的處理功能,無非根據當前的數據自動生成下一個月數據的初始值。下面我以一個簡化了例子來說明,例子是記錄員工每個月工分變化中,我們以12月的數據生成下個月的月初數據。
------------------------------------
-- Author: happyflsytone
-- Date:2008-10-02 16:39:39
-- Description:根據當前數據生成下個月的月初數據,並刪除歷史數據
------------------------------------
DECLARE @s TABLE([年] INT,[月] INT,[工號] INT,[上月工分值] INT,[本月工分值] INT);
INSERT @s SELECT 2008,12,1,10,11;
INSERT @s SELECT 2008,12,2,11,12;
INSERT @s SELECT 2008,12,3,11,13;
INSERT @s SELECT 2008,12,4,3,5;
INSERT @s SELECT 2008,12,5,1,7;
INSERT @s SELECT 2008,12,6,2,11;
DELETE FROM @s
OUTPUT CASE WHEN DELETED.[月] = 12 THEN DELETED.[年]+ 1 ELSE DELETED.[年] END ,
CASE WHEN DELETED.[月] = 12 THEN 1 ELSE DELETED.[月] + 1 END ,
DELETED.[工號],DELETED.[本月工分值],NULL as [上月工分值]
INTO @s;
SELECT *
FROM @s
ORDER BY 1,2,3
/*
年 月 工號 上月工分值 本月工分值
----------- ----------- ----------- ----------- -----------
2009 1 1 11 NULL
2009 1 2 12 NULL
2009 1 3 13 NULL
2009 1 4 5 NULL
2009 1 5 7 NULL
2009 1 6 11 NULL
(6 行受影響)
*/
2、根據業務規則的需要保證數據完整性。
在這個例程裏我假設在更新員工的最後登錄時間同時增加一條日誌信息。先看測試數據:
------------------------------------
-- Author: happyflsytone
-- Date:2008-10-02 16:39:39
-- Description:員工登錄時更新員工表的最後登錄時間,同時在日誌表增加一條登錄信息
------------------------------------
-- 操作員信息表(本例只關心最後登錄時間,所以員工的信息不深入表述)
DECLARE @P TABLE([工號] INT,[姓名] varchar(16),[最後登錄時間] datetime);
INSERT @P SELECT 1,'test1',getdate() - 1;
INSERT @P SELECT 2,'test2',getdate() - 1;
INSERT @P SELECT 3,'test3',getdate() - 1;
INSERT @P SELECT 4,'test4',getdate() - 1;
INSERT @P SELECT 5,'test5',getdate() - 1;
INSERT @P SELECT 6,'test6',getdate() - 1;
-- 操作員操作日誌(象徵性列舉一些字段)
DECLARE @LOG TABLE([工號] INT,[操作時間] DATETIME,[操作類型] CHAR(6),[操作說明] VARCHAR(200));
--模擬工號爲3的操作員登錄,並記錄相應日誌
UPDATE @p
SET [最後登錄時間] = GETDATE()
OUTPUT DELETED.[工號],DELETED.[最後登錄時間],'出艙','成功出艙行走,身體狀況良好,儀器工作正常,請主席放心!'
INTO @log
WHERE [工號] = 3;
-- 查看日誌
SELECT *
FROM @LOG;
/*
工號 操作時間 操作類型 操作說明
----------- ----------------------- ------ ------------------------------------
3 2008-10-01 17:06:58.790 出艙 成功出艙行走,身體狀況良好,儀器工作正常,請主席放心!
(1 行受影響)
*/
注:其實我們可以通過這個OUTPUT_CLAUSE嚮應用程序提供數據操作的歷史信息,或是把數據緩存在表變量中以備程序再次調用,關於這方面的例子就不多說明,因爲這是最基本的OUTPUT_CLAUSE應用。
3、標識列或計算列方面的應用
對於標識列我們可能通過@@IDENTITY、SCOPE_IDENTITY 和 IDENT_CURRENT 幾個相似的函數獲得,他們都返回插入到表的 IDENTITY 列的最後一個值(本身這幾個函數還是有差異的,主要是它們的作用域,請查聯機幫助)。我們注意到它們只是返回最後一個值,對於批量時就無能無力了。對於實時併發多的系統時我們可以利用OUTPUT_CLAUSE語句把標識列的值提取出來。
------------------------------------
-- Author: happyflsytone
-- Date:2008-10-02 16:39:39
------------------------------------
CREATE TABLE ScrapReason(scrapreasonid INT IDENTITY,[name] VARCHAR(50),modifieddate DATETIME)
;
--接受標識列值的表變量
DECLARE @MyTableVar TABLE( ScrapReasonID SMALLINT,
Name VARCHAR(50),
ModifiedDate DATETIME);
--模擬插入數據
INSERT ScrapReason
OUTPUT INSERTED.ScrapReasonID, INSERTED.Name, INSERTED.ModifiedDate
INTO @MyTableVar
SELECT N'Operator IDENTITY', GETDATE()
FROM sys.objects ;
--查看記錄的標識列數據
SELECT ScrapReasonID, Name, ModifiedDate FROM @MyTableVar;
GO
drop table ScrapReason;
/*
ScrapReasonID Name ModifiedDate
------------- -------------------------------------------------- -----------------------
1 Operator IDENTITY 2008-10-02 17:42:19.000
2 Operator IDENTITY 2008-10-02 17:42:19.000
3 Operator IDENTITY 2008-10-02 17:42:19.000
4 Operator IDENTITY 2008-10-02 17:42:19.000
5 Operator IDENTITY 2008-10-02 17:42:19.000
6 Operator IDENTITY 2008-10-02 17:42:19.000
.....
.....
62 Operator IDENTITY 2008-10-02 17:42:19.000
63 Operator IDENTITY 2008-10-02 17:42:19.000
(63 行受影響)
*/
下面我們再來看看觸發器使用OUTPUT_CLAUSE的情況,
------------------------------------
-- Author: happyflsytone
-- Date:2008-10-02 16:39:39
------------------------------------
CREATE TABLE TA(
scrapreasonid INT IDENTITY PRIMARY KEY,
[name] VARCHAR(50),
modifieddate DATETIME
)
;
CREATE TABLE TB(
ID INT REFERENCES TA(SCRAPREASONID),
[name] VARCHAR(50),
MODIFIEDDATE DATETIME
);
GO
CREATE TRIGGER TR_INSERT
ON TA
INSTEAD OF INSERT
AS
BEGIN
--接受標識列值的表變量
DECLARE @MyTableVar TABLE( ID INT,
[NAME] VARCHAR(10),
ModifiedDate DATETIME);
INSERT TA
OUTPUT INSERTED.scrapreasonid,INSERTED.[NAME],INSERTED.ModifiedDate
INTO @MyTableVar
SELECT [name],modifieddate FROM INSERTED
INSERT INTO TB SELECT * FROM @MyTableVar
END
GO
--模擬插入數據
INSERT TA SELECT 'TEST',GETDATE();
INSERT TA SELECT 'TEST2',GETDATE();
--查看記錄的標識列數據
SELECT * FROM TB;
/*
ID name MODIFIEDDATE
----------- -------------------------------------------------- -----------------------
1 TEST 2008-10-02 17:53:46.780
2 TEST2 2008-10-02 17:53:46.870
(2 行受影響)
*/
DROP TABLE TB,TA;
最後說明一下使用OUTPUT子句的注意事項:
以下語句中不支持 OUTPUT 子句:
1、引用本地分區視圖、分佈式分區視圖或遠程表的 DML 語句。
2、包含 EXECUTE 語句的 INSERT 語句。
3、不能將 OUTPUT INTO 子句插入視圖或行集函數。
4、參數或變量作爲 UPDATE 語句的一部分進行了修改,則 OUTPUT 子句將始終返回語句執行之前的參數或變量的值而不是已修改的值