[学习SQL SERVER 2005系列]关于INSERT、UPDATE或DELETE的OUTPUT及OUTPUT...INTO...用法

Author: 胡林

Date: 2008年10月05日 15:14:00

在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; 
    
    

    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 

    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  子句将始终返回语句执行之前的参数或变量的值而不是已修改的值

  

                          Flystone   于常州

 

 

 

下一篇:(胡林)[学习SQL SERVER 2005系列]感受新功能一:PIVOT