2012-12-31 45 views
4

我必须从temptable中选择记录并插入或更新到mastertable。如果一个字段被改变了,那么我就需要用下面的更新我history表:临时表到主表插入或更新审计和日志记录

  • RecordID
  • ColumnChanged
  • OldValue
  • NewValue

当执行插入或更新从temptablemastertable,由于外键vi而发生的任何错误Olations(或任何其他错误)应记录在error表中。

我需要更改和记录错误的历史记录,以便如果单个记录失败,我可以重新运行该过程。

我怎样才能做到这一点使用T-SQL?任何代码片段都会有所帮助。

下面是我的工作表列:

  1. 不是Temptable
    COL1,COL2,COL3,COL4,COL5

  2. MASTERTABLE
    COL1,COL2 ,COL3

  3. HISTORY
    的recordId,ColumnChanged,的OldValue,的NewValue

  4. ERROR
    错误码,ERRORMSG

我需要使用一个光标,如我通过每个记录需要循环执行时插入或更新,以及记录由于错误数据而在插入或更新过程中发生的任何错误。

我需要将flagcolumn标记为所有成功插入或更新的“Y”,对于所有失败的记录标记为“N”,以便我可以在更正数据后重新加载它们。我已经使用sqlbulkcopy加载数据到temptable

+1

对于这种类型的审计,你不应该需要一个光标,根据SQL Server的什么味道你你应该能够使用输出子句http://msdn.microsoft.com/en-us/library/ms177564.aspx – gh9

+0

你没有指定哪些是TEMPTABLE和MASTERTABLE中的主键(即使有不任何定义的主键,我们需要知道哪些列应被视为主键的一部分)。此外,TEMPTABLE中的列数多于MASTERTABLE;我们应该忽略额外的列吗? –

回答

2

尝试这一个 -

模式:

CREATE TABLE dbo.HistoryTable 
(
    HistoryTableID INT IDENTITY(1,1) PRIMARY KEY NOT NULL, 
    PKRecordID INT NOT NULL, 
    ColumnChanged VARCHAR(50) NOT NULL, 
    OldValue VARCHAR(10) NOT NULL, 
    NewValue VARCHAR(10) NOT NULL, 
    ChangedDate DATETIME NOT NULL DEFAULT (GETDATE()) 
) 
GO 

CREATE TABLE dbo.ForeignKeyTableCOL2 (COL2 VARCHAR(10) PRIMARY KEY NOT NULL) 
GO 

CREATE TABLE dbo.ForeignKeyTableCOL1 (COL1 VARCHAR(10) PRIMARY KEY NOT NULL) 
GO 

CREATE TABLE dbo.ErrorTable 
(
    ErrorTableID INT IDENTITY(1,1) PRIMARY KEY NOT NULL, 
    PKRecordID INT NOT NULL, 
    TableName VARCHAR(50) NOT NULL, 
    TablePK INT NOT NULL, 
    ErrorDateTime DATETIME NOT NULL DEFAULT (GETDATE()), 
    ErrorCode INT NOT NULL, 
    ErrorMsg VARCHAR(2000) NOT NULL 
) 
GO 

CREATE TABLE dbo.TempTable 
(
    TempTableID INT IDENTITY(1,1) NOT NULL, 
    PKRecordID INT NOT NULL, 
    COL1 VARCHAR(10) NOT NULL, 
    COL2 VARCHAR(10) NOT NULL, 
    COL3 VARCHAR(10) NOT NULL, 
    COL4 VARCHAR(10) NOT NULL, 
    COL5 VARCHAR(10) NOT NULL, 
    Success CHAR(1) NOT NULL DEFAULT ('N') 
) 
GO 

CREATE TABLE dbo.MasterTable 
(
    PKRecordID INT NOT NULL, 
    COL1 VARCHAR(10) NOT NULL, 
    COL2 VARCHAR(10) NOT NULL, 
    COL3 VARCHAR(10) NOT NULL, 
    COL4 VARCHAR(10) NOT NULL, 
    COL5 VARCHAR(10) NOT NULL 
) 
GO 

ALTER TABLE dbo.MasterTable WITH CHECK ADD CONSTRAINT FK_MasterTable_ForeignKeyTableCOL1 FOREIGN KEY(COL1) 
REFERENCES dbo.ForeignKeyTableCOL1 (COL1) 

ALTER TABLE dbo.MasterTable CHECK CONSTRAINT FK_MasterTable_ForeignKeyTableCOL1 

ALTER TABLE dbo.MasterTable WITH CHECK ADD CONSTRAINT FK_MasterTable_ForeignKeyTableCOL2 FOREIGN KEY(COL2) 
REFERENCES dbo.ForeignKeyTableCOL2 (COL2) 

ALTER TABLE dbo.MasterTable CHECK CONSTRAINT FK_MasterTable_ForeignKeyTableCOL2 

INSERT dbo.ForeignKeyTableCOL1 (COL1) 
VALUES ('A'), ('B'), ('C') 
INSERT dbo.ForeignKeyTableCOL2 (COL2) 
VALUES ('A'), ('B'), ('C') 

INSERT dbo.TempTable (PKRecordID, COL1, COL2, COL3, COL4, COL5) 
VALUES 
    (1, 'A', 'A', 'A', 'A', 'A'), 
    (2, 'B', 'B', 'B', 'B', 'B'), 
    (3, 'C', 'C', 'C', 'C', 'C'), 
    (1, 'D', 'A', 'A', 'A', 'A'), 
    (1, 'A', 'D', 'A', 'A', 'A'), 
    (1, 'D', 'D', 'A', 'A', 'A'), 
    (2, 'A', 'B', 'B', 'B', 'B'), 
    (3, 'A', 'B', 'C', 'C', 'C'), 
    (4, 'D', 'D', 'D', 'D', 'D') 

查询:

SET NOCOUNT ON; 

DECLARE 
     @PKRecordID INT 
    , @COL1 VARCHAR(10) 
    , @COL2 VARCHAR(10) 
    , @COL3 VARCHAR(10) 
    , @COL4 VARCHAR(10) 
    , @COL5 VARCHAR(10) 
    , @TempTableID INT 
    , @New_Row XML 
    , @Old_Row XML 

DECLARE cur CURSOR LOCAL FAST_FORWARD READ_ONLY FOR 
    SELECT 
      t.TempTableID 
     , t.PKRecordID 
     , t.COL1 
     , t.COL2 
     , t.COL3 
     , t.COL4 
     , t.COL5 
     , New_Row = CAST('<r><s>' + t.COL1 + '</s><s>' + t.COL2 + '</s><s>' + t.COL3 + '</s><s>' + t.COL4 + '</s><s>' + t.COL5 + '</s></r>' AS XML) 
     , Old_Row = CAST('<r><s>' + m.COL1 + '</s><s>' + m.COL2 + '</s><s>' + m.COL3 + '</s><s>' + m.COL4 + '</s><s>' + m.COL5 + '</s></r>' AS XML) 
    FROM dbo.TempTable t 
    LEFT JOIN dbo.MasterTable m ON t.PKRecordID = m.PKRecordID 

OPEN cur 

FETCH NEXT FROM cur INTO 
     @TempTableID 
    , @PKRecordID 
    , @COL1 
    , @COL2 
    , @COL3 
    , @COL4 
    , @COL5 
    , @New_Row 
    , @Old_Row 

WHILE @@FETCH_STATUS = 0 BEGIN 

    BEGIN TRY 

     IF @Old_Row IS NOT NULL BEGIN 

      UPDATE dbo.MasterTable 
      SET 
        COL1 = @COL1 
       , COL2 = @COL2 
       , COL3 = @COL3 
       , COL4 = @COL4 
       , COL5 = @COL5 
      WHERE PKRecordID = @PKRecordID 

      INSERT dbo.HistoryTable 
      (
        PKRecordID 
       , ColumnChanged 
       , OldValue 
       , NewValue 
      ) 
      SELECT 
        @PKRecordID 
       , 'COL' + CAST(new_id AS VARCHAR(5)) 
       , old_value 
       , new_value 
      FROM (
       SELECT 
         new_value = n.value('(.)1', 'VARCHAR(10)') 
        , new_id = 1 + n.value('for $i in . return count(../*. << $i)', 'int') 
        , old_value = o.value('(.)1', 'VARCHAR(10)') 
        , old_id = 1 + o.value('for $i in . return count(../*. << $i)', 'int') 
       FROM (SELECT a = 1) d 
       CROSS APPLY @New_Row.nodes('/r/s') t(n) 
       CROSS APPLY @Old_Row.nodes('/r/s') k(o) 
      ) data 
      WHERE new_id = old_id 
       AND NULLIF(new_value, '') != NULLIF(old_value, '') 

      UPDATE dbo.TempTable 
      SET Success = 'Y' 
      WHERE TempTableID = @TempTableID 

     END 
     ELSE BEGIN 

      INSERT dbo.MasterTable 
      (
        PKRecordID 
       , COL1 
       , COL2 
       , COL3 
       , COL4 
       , COL5 
      ) 
      SELECT 
        @PKRecordID 
       , @COL1 
       , @COL2 
       , @COL3 
       , @COL4 
       , @COL5   

      UPDATE dbo.TempTable 
      SET Success = 'Y' 
      WHERE TempTableID = @TempTableID 

     END 

    END TRY 
    BEGIN CATCH 

     INSERT dbo.ErrorTable 
     (
       PKRecordID 
      , TableName 
      , TablePK 
      , ErrorDateTime 
      , ErrorCode 
      , ErrorMsg 
     ) 
     SELECT 
       @PKRecordID 
      , 'TempTable' 
      , @TempTableID 
      , GETDATE() 
      , ERROR_NUMBER() 
      , ERROR_MESSAGE() 

    END CATCH 

    FETCH NEXT FROM cur INTO 
      @TempTableID 
     , @PKRecordID 
     , @COL1 
     , @COL2 
     , @COL3 
     , @COL4 
     , @COL5 
     , @New_Row 
     , @Old_Row 

END 

CLOSE cur 
DEALLOCATE cur 

而这可能对您有所帮助:

CREATE TRIGGER ... 
    ON ... 
    INSTEAD OF INSERT, UPDATE 
AS 
BEGIN 

    SET NOCOUNT ON 
    SET XACT_ABORT ON 

    DECLARE @DocumentUID UNIQUEIDENTIFIER 
    ... 

    DECLARE cur CURSOR FORWARD_ONLY READ_ONLY LOCAL FOR 
     SELECT DocumentUID, ... 
     FROM INSERTED 

    OPEN cur 

    FETCH NEXT FROM cur INTO @DocumentUID, ... 

    WHILE @@FETCH_STATUS = 0 BEGIN 

     DECLARE 
       @BeforeChange NVARCHAR(MAX) 
      , @AfterChange NVARCHAR(MAX) 

     SELECT 
       @BeforeChange = (
       SELECT * 
       FROM DELETED 
       WHERE DocumentUID = @DocumentUID 
       FOR XML RAW, ROOT 
      ) 
      , @AfterChange = (
       SELECT * 
       FROM INSERTED 
       WHERE DocumentUID = @DocumentUID 
       FOR XML RAW, ROOT 
      ) 

     IF EXISTS(
      SELECT 1 
      FROM dbo.Documents 
      WHERE DocumentUID = @DocumentUID 
     ) 
     BEGIN 

      INSERT INTO ... 
      SELECT @BeforeChange, @AfterChange 

     END 
     ELSE BEGIN 

      ... 

     END 

     FETCH NEXT FROM cur INTO @DocumentUID, ... 

    END 

    CLOSE cur 
    DEALLOCATE cur 

END 
0

安装我们的模式和表进行测试

SET NOCOUNT ON 

IF OBJECT_ID('TempTable', 'U') IS NOT NULL DROP TABLE TempTable; 
IF OBJECT_ID('MasterTable', 'U') IS NOT NULL DROP TABLE MasterTable; 
IF OBJECT_ID('ForeignKeyTableCOL1', 'U') IS NOT NULL DROP TABLE ForeignKeyTableCOL1; 
IF OBJECT_ID('ForeignKeyTableCOL2', 'U') IS NOT NULL DROP TABLE ForeignKeyTableCOL2; 
IF OBJECT_ID('HistoryTable', 'U') IS NOT NULL DROP TABLE HistoryTable; 
IF OBJECT_ID('ErrorTable', 'U') IS NOT NULL DROP TABLE ErrorTable; 

CREATE TABLE ForeignKeyTableCOL1 (COL1 varchar(10) PRIMARY KEY) 
CREATE TABLE ForeignKeyTableCOL2 (COL2 varchar(10) PRIMARY KEY) 
CREATE TABLE TempTable (TempTableID int IDENTITY(1,1), PKRecordID int, COL1 varchar(10), COL2 varchar(10), COL3 varchar(10), COL4 varchar(10), COL5 varchar(10), Success char(1)) 
CREATE TABLE MasterTable (PKRecordID int PRIMARY KEY, COL1 varchar(10), COL2 varchar(10), COL3 varchar(10), COL4 varchar(10), COL5 varchar(10)) 
ALTER TABLE MasterTable ADD CONSTRAINT FK_MasterTable_ForeignKeyTableCOL1 FOREIGN KEY (COL1) REFERENCES ForeignKeyTableCOL1 (COL1) 
ALTER TABLE MasterTable ADD CONSTRAINT FK_MasterTable_ForeignKeyTableCOL2 FOREIGN KEY (COL2) REFERENCES ForeignKeyTableCOL2 (COL2) 
CREATE TABLE HistoryTable (HistoryTableID int IDENTITY(1,1) PRIMARY KEY, PKRecordID int, ColumnChanged varchar(50), OldValue varchar(10), NewValue varchar(10)) 
CREATE TABLE ErrorTable (ErrorTableID int IDENTITY(1,1) PRIMARY KEY, PKRecordID int, TableName varchar(50), TablePK int, ErrorDateTime datetime, ErrorCode int, ErrorMsg nvarchar(4000)) 
GO 

INSERT ForeignKeyTableCOL1 SELECT 'A' UNION SELECT 'B' UNION SELECT 'C' 
INSERT ForeignKeyTableCOL2 SELECT 'A' UNION SELECT 'B' UNION SELECT 'C' 

INSERT TempTable SELECT 1, 'A', 'A', 'A', 'A', 'A', 'N' 
INSERT TempTable SELECT 2, 'B', 'B', 'B', 'B', 'B', 'N' 
INSERT TempTable SELECT 3, 'C', 'C', 'C', 'C', 'C', 'N' 
INSERT TempTable SELECT 1, 'D', 'A', 'A', 'A', 'A', 'N' 
INSERT TempTable SELECT 1, 'A', 'D', 'A', 'A', 'A', 'N' 
INSERT TempTable SELECT 1, 'D', 'D', 'A', 'A', 'A', 'N' 
INSERT TempTable SELECT 2, 'A', 'B', 'B', 'B', 'B', 'N' 
INSERT TempTable SELECT 3, 'A', 'B', 'C', 'C', 'C', 'N' 
INSERT TempTable SELECT 4, 'D', 'D', 'D', 'D', 'D', 'N' 

SET NOCOUNT OFF 

执行INSERTSUPDATES与审计/错误日志

DECLARE @PKRecordID int, @COL1 varchar(10), @COL2 varchar(10), @COL3 varchar(10), @COL4 varchar(10), @COL5 varchar(10), @TempTableID int 
DECLARE @PKRecordID_OLD int, @COL1_OLD varchar(10), @COL2_OLD varchar(10), @COL3_OLD varchar(10), @COL4_OLD varchar(10), @COL5_OLD varchar(10) 
DECLARE temp_cursor CURSOR FOR 
SELECT TempTableID, PKRecordID, COL1, COL2, COL3, COL4, COL5 
    FROM TempTable 
    ORDER BY TempTableID; 
OPEN temp_cursor 

FETCH NEXT FROM temp_cursor 
INTO @TempTableID, @PKRecordID, @COL1, @COL2, @COL3, @COL4, @COL5 

WHILE @@FETCH_STATUS = 0 
    BEGIN 
    --SELECT @idx, @COL1, @COL2, @COL3, @COL4, @COL5 


    BEGIN TRY 
     IF EXISTS (SELECT * FROM MasterTable WHERE PKRecordID = @PKRecordID) 
      BEGIN 
      SELECT @COL1_OLD = COL1 
        ,@COL2_OLD = COL2 
        ,@COL3_OLD = COL3 
        ,@COL4_OLD = COL4 
        ,@COL5_OLD = COL5 
       FROM MasterTable 
      WHERE PKRecordID = @PKRecordID 


      UPDATE MasterTable 
       SET COL1 = @COL1 
        ,COL2 = @COL2 
        ,COL3 = @COL3 
        ,COL4 = @COL4 
        ,COL5 = @COL5 
      WHERE PKRecordID = @PKRecordID 


      INSERT HistoryTable (PKRecordID, ColumnChanged, OldValue, NewValue) 
      SELECT @PKRecordID, 'COL1', @COL1_OLD, @COL1 
      WHERE EXISTS (SELECT @COL1 EXCEPT SELECT @COL1_OLD) 

      INSERT HistoryTable (PKRecordID, ColumnChanged, OldValue, NewValue) 
      SELECT @PKRecordID, 'COL2', @COL2_OLD, @COL2 
      WHERE EXISTS (SELECT @COL2 EXCEPT SELECT @COL2_OLD) 

      INSERT HistoryTable (PKRecordID, ColumnChanged, OldValue, NewValue) 
      SELECT @PKRecordID, 'COL3', @COL3_OLD, @COL3 
      WHERE EXISTS (SELECT @COL3 EXCEPT SELECT @COL3_OLD) 

      INSERT HistoryTable (PKRecordID, ColumnChanged, OldValue, NewValue) 
      SELECT @PKRecordID, 'COL4', @COL4_OLD, @COL4 
      WHERE EXISTS (SELECT @COL4 EXCEPT SELECT @COL4_OLD) 

      INSERT HistoryTable (PKRecordID, ColumnChanged, OldValue, NewValue) 
      SELECT @PKRecordID, 'COL5', @COL5_OLD, @COL5 
      WHERE EXISTS (SELECT @COL5 EXCEPT SELECT @COL5_OLD) 


      UPDATE TempTable 
       SET Success = 'Y' 
      WHERE TempTableID = @TempTableID 

      END 
     ELSE 
      BEGIN 
      INSERT MasterTable (PKRecordID, COL1, COL2, COL3, COL4, COL5) 
      SELECT @PKRecordID, @COL1, @COL2, @COL3, @COL4, @COL5   


      UPDATE TempTable 
       SET Success = 'Y' 
      WHERE TempTableID = @TempTableID 
     END 
    END TRY 
    BEGIN CATCH 
     INSERT ErrorTable (PKRecordID, TableName, TablePK, ErrorDateTime, ErrorCode, ErrorMsg) 
     SELECT @PKRecordID, 'TempTable', @TempTableID, GETDATE(), ERROR_NUMBER(), ERROR_MESSAGE() 
    END CATCH 


    FETCH NEXT FROM temp_cursor 
    INTO @TempTableID, @PKRecordID, @COL1, @COL2, @COL3, @COL4, @COL5 
END 
CLOSE temp_cursor; 
DEALLOCATE temp_cursor; 

-- VIEW OUTPUT 
SELECT * FROM MasterTable 
SELECT * FROM ErrorTable 
SELECT * FROM HistoryTable 
SELECT * FROM TempTable