2

我每周都会收到一个 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 相当陌生,但经过一些研究,似乎触发器可能是采用的方法,但欢迎任何有关如何解决此问题的建议。

谢谢

马特

4

1 回答 1

1

合并语句有一个输出子句,它将受影响的行输出到表中,或者作为查询的结果。

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

由于输出子句可以是查询的结果,因此您可以将其嵌套为内部查询。我已经用它来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 
于 2012-11-23T20:39:31.463 回答