1. 程式人生 > >SQL Server--OUTPUT及OUTPUT...INTO... 在 INSERT、UPDATE 或 DELETE的用法

SQL Server--OUTPUT及OUTPUT...INTO... 在 INSERT、UPDATE 或 DELETE的用法

在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 子句將始終返回語句執行之前的引數或變數的值而不是已修改的值