【问题标题】:Updating / Inserting row only if it's different from current value仅当行与当前值不同时才更新/插入行
【发布时间】:2019-08-19 11:41:54
【问题描述】:

我有一个桌面应用程序,它将包含产品信息的数据表发送到 SQL Server 数据库。我只想为与接收行不同的列更新表。我想知道正确的方法是:

  • 检查接收到的数据表中的值是否与实际db表中的值不同,如果是则更新表
  • 如果值已修改,则将新行插入包含日志的表中

我目前在做什么:

我从新变量 (@OldValue) 内的数据表中选择当前值,然后将其与接收到的值 (@NewValue) 进行比较。如果值不同,我只需更新列并将新记录插入日志表。

DECLARE @ProductId = 333
IF @OldValue <> @NewValue
BEGIN
    UPDATE dbo.ProductTable
       SET Column1 = @NewValue
    WHERE
       ProductId = @ProductId




 INSERT INTO 
     dbo.Logs
 VALUES
     (@ProductId, @ModifiedColumn, @OldValue, @NewValue, GETDATE())

END

我对需要更新的每一列(如果数据已更改)运行检查,但收到的数据表包含大约 15 列,我认为必须有更优雅的解决方案。

任何有用的提示将不胜感激。

【问题讨论】:

  • 您的表格是否只有 1 列在此处更新?您的日志表和UPDATE 暗示只有Column 正在更新;对吗?
  • 不,目标表和源表都包含 15 列,包含 ProductId、ProductName、ProductPrice 等数据@Larnu
  • 您使用的是什么版本的 SQL Server?
  • @ZoharPeled 2012
  • 你能在客户端做任何预处理吗?仅发回那些已更改的行。您可以使用触发器来更新日志文件,而不是使用单独的脚本。

标签: sql-server tsql sql-server-2012


【解决方案1】:

我可能会做类似下面的事情。目前这仅适用于两列,但应该清楚如何添加其他列。

DECLARE @UpdateResult TABLE
(
ProductId INT PRIMARY KEY,
dCol1 VARCHAR(50), /*These should be whatever the datatype of the relevant columns in dbo.Logs are*/
iCol1 VARCHAR(50), 
dCol2 VARCHAR(50), 
iCol2 VARCHAR(50)
);


UPDATE PT
SET PT.Col1 = Tvp.Col1, 
    PT.Col2 = Tvp.Col2 
    OUTPUT inserted.ProductId, 
           deleted.Col1, 
           inserted.Col1 ,
           deleted.Col2, 
           inserted.Col2 INTO @UpdateResult(ProductId, dCol1, iCol1, dCol2,iCol2 )
FROM dbo.ProductTable AS PT
JOIN @Tvp Tvp ON Tvp.ProductId = PT.ProductId
/*Only update where at least one column is different*/
WHERE EXISTS (SELECT PT.Col1, PT.Col2 
              EXCEPT 
              SELECT Tvp.Col1, Tvp.Col2)


INSERT INTO dbo.Logs
SELECT ProductId, ModifiedColumn, OldValue, NewValue, getdate()
FROM @UpdateResult
CROSS APPLY (VALUES
                    ('Col1',dCol1, iCol1),
                    ('Col2',dCol2, iCol2)
            ) unpvt(ModifiedColumn, OldValue, NewValue )
WHERE EXISTS (SELECT OldValue EXCEPT SELECT NewValue)

【讨论】:

  • 我会看看你的解决方案,稍后告诉你,谢谢;)
  • 这样就完成了。谢谢~!
【解决方案2】:

我经常看到这种问题。在大多数情况下,我们连接所有列,用固定字符串(如'||')分隔,然后对两个表应用HASHBYTES('MD5',MyConcatenatedValue) 函数。如果 Hash 相同,我们不更新行。如果不同,我们更新它...

如果我重用你的代码,它可能会变成这样:

DECLARE @ProductId = 333
SET @OldValue =     SELECT(HASHBYTES('MD5',Column1+'||'+Column2+'||'+Column3+'||'+Column4+'||'+Column5+'||'+Column6) FROM dbo.ProductTable) -- continue this logic for each column
SET @NewValue =     SELECT(HASHBYTES('MD5',Column1+'||'+Column2+'||'+Column3+'||'+Column4+'||'+Column5+'||'+Column6) FROM dbo.SourceTable) -- I assume you have a table with new values stored
IF @OldValue <> @NewValue
BEGIN
    UPDATE Product 
    FROM dbo.ProductTable Product
    INNER JOIN dbo.SourceTable Source 
       ON Product.ProductId = Source.ProductId
       SET Product.Column1 = Source.Column1
          ,Product.Column2 = Source.Column2
          ,Product.Column3 = Source.Column3
          ,Product.Column4 = Source.Column4
          ,Product.Column5 = Source.Column5
          ,Product.Column6 = Source.Column6              
    WHERE
      ProductId = @ProductId
END

但是,您不能轻松地记录被替换的列,因为您对所有列进行了一次检查。也许您应该考虑实施临时表。这是一篇关于它的文章:

https://docs.microsoft.com/fr-fr/sql/relational-databases/tables/temporal-tables?view=sql-server-2017

希望对您有所帮助,如果您需要更多详细信息,请告诉我。

【讨论】:

  • 有见地,但遗憾的是没有回答这个问题:(
  • 我可能误解了,但您想知道一个优雅的解决方案来检查您是否需要更新包含 15 列以上的行。我刚刚为您提供了分析领域中使用的最佳实践。我很高兴知道我错过了关于您的问题的内容。直接需要代码吗?
  • 我没有问这个问题——我试图帮助新用户了解为什么没有投票:) 是的,我认为至少有一个例子说明如何使用实际SQL 代码会很有用。您概述了一种策略而非解决方案。
  • 好的,我现在明白了。我试图根据一些推测添加一个具体的例子。感谢您的帮助和反馈。 :-)
  • 是的,这现在得到了我的 +1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-04-30
  • 1970-01-01
  • 1970-01-01
  • 2018-12-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多