批量插入后的SQL触发器

时间:2012-11-23 20:31:54

标签: sql sql-server triggers sql-server-2012

我每周都会收到一个CSV文件,我使用BULK INSERT导入我的SQL数据库。我插入一个临时表,然后通过插入新记录并更新任何已修改的记录将其与主表合并。代码是:

BULK INSERT dbo.temp
FROM 'C:\Users\Administrator\Documents\20120125.csv'
WITH (FIELDTERMINATOR = ',', ROWTERMINATOR = '\n' );

MERGE dbo.main AS TargetTable                            
USING dbo.temp AS SourceTable                    
ON (TargetTable.name = SourceTable.name)                
WHEN NOT MATCHED BY TARGET                              
    THEN INSERT (name, age, gender, father, mother, teacher, mathsrating, historyrating, sciencerating, englishrating)
        VALUES(SourceTable.name, SourceTable.age, SourceTable.gender, SourceTable.father, SourceTable.mother, SourceTable.teacher, SourceTable.mathsrating, SourceTable.historyrating, SourceTable.sciencerating, SourceTable.englishrating)
WHEN MATCHED                                            
    THEN UPDATE SET
        TargetTable.name = SourceTable.name,
        TargetTable.age = SourceTable.age,
        TargetTable.gender = SourceTable.gender,
        TargetTable.father = SourceTable.father,
        TargetTable.mother = SourceTable.mother,
        TargetTable.teacher = SourceTable.teacher,
        TargetTable.mathsrating = SourceTable.mathsrating,
        TargetTable.historyrating = SourceTable.historyrating,
        TargetTable.sciencerating = SourceTable.sciencerating,
        TargetTable.englishrating = SourceTable.englishrating;

DELETE FROM dbo.temp

我想要实现的是将更新覆盖的记录存储在具有“先前”值的新表中,以便我具有已更改内容的历史记录。我对SQL很新,但研究了一下似乎是一个触发可能是采取的方法,但欢迎任何关于如何处理这个问题的建议。

由于

马特

1 个答案:

答案 0 :(得分:1)

merge语句有output clause,它会将受影响的行输出到表中,或者作为查询的结果。

您还可以在匹配时部分指定额外条件。在这种情况下,如果所有值都与现有行相同,则使用它来确保不会更新行。如果列可以为空,那么这很麻烦,因为(1 != Null)会返回Null。对于您的评分,我假设不能为负,您可以IsNull(s.rating, -1) != IsNull(t.rating, -1)查看是否已更改

由于output子句可以是查询的结果,因此可以将其嵌套为内部查询。我已使用此功能将UpdateTimestamp添加到您的历史记录表中。

示例:http://sqlfiddle.com/#!6/1651a/2

为什么需要进行空检查的示例:http://sqlfiddle.com/#!6/bd99b/2

Insert Into 
  history_table  
Select
  GetDate(), [name], age, gender, father, mother, teacher, 
  mathsrating, historyrating, sciencerating, englishrating
From (
  Merge
    dbo.main As t
  Using
    dbo.temp AS s
      On (t.[name] = s.[name])
  When Not Matched By Target Then
    Insert (
      [name], age, gender, father, mother, teacher, 
      mathsrating, historyrating, sciencerating, englishrating
    ) values (
      s.[name], s.age, s.gender, s.father, s.mother, s.teacher, 
      s.mathsrating, s.historyrating, s.sciencerating, s.englishrating
    )
  When Matched And -- assume ratings can't be negative, but can be null
    t.age != s.age Or
    t.gender != s.gender Or
    t.father != s.father Or
    t.mother != s.mother Or
    t.teacher != s.teacher Or
    IsNull(t.mathsrating, -1) != IsNull(s.mathsrating, -1) Or
    IsNull(t.historyrating, -1) != IsNull(s.historyrating, -1) Or
    IsNull(t.sciencerating, -1) != IsNull(s.sciencerating, -1) Or
    IsNull(t.englishrating, -1) != IsNull(s.englishrating, -1)
  Then Update
    Set
      t.[name] = s.[name],
      t.age = s.age,
      t.gender = s.gender,
      t.father = s.father,
      t.mother = s.mother,
      t.teacher = s.teacher,
      t.mathsrating = s.mathsrating,
      t.historyrating = s.historyrating,
      t.sciencerating = s.sciencerating,
      t.englishrating = s.englishrating
  Output
      $action as Action, deleted.*
  ) as Updates