T-SQL条件更新(v2)

时间:2009-10-10 04:05:47

标签: sql sql-server tsql sql-update

我有一张桌子:

Message (MessageID int, Subject nvarchar(100), Body nvarchar(max))

在UI上更新消息后,我调用存储过程来更新该表。在某些情况下,用户可能只更新主题,在其他情况下只是正文。我希望这个存储过程仅更新已更改的内容,因此我也会传递标记,显示主题或正文是否已更新:

create proc UpdateMessage(
  @MessageID int, 
  @Subject nvarchar(100), 
  @Body nvarchar(max),
  @SubjectChanged bit,
  @BodyChanged bit)

现在我很困惑如何构建条件UPDATE语句。我的第一个想法是使用CASE

Update [Message] 
SET 
CASE WHEN @SubjectChanged = 1 THEN [Subject] = @Subject ELSE 1=1 END,
CASE WHEN @BodyChanged = 1 THEN Body = @Body ELSE 1=1 END,
WHERE MessageID = @MessageID

...但这似乎不是一个正确的语法,因为CASE必须是一个分配的右侧。

我有什么想法可以做到这一点? (请记住,实际上有6个参数可以更新,而不是两个)

8 个答案:

答案 0 :(得分:27)

创建语句所需的语法是:

Update [Message] 
SET    [Subject] = CASE WHEN @SubjectChanged = 1 THEN @Subject ELSE [Subject] END,
       Body = CASE WHEN @BodyChanged = 1 THEN @Body ELSE Body END
WHERE  MessageID = @MessageID

如果你仍然想在所有建议之后坚持下去。

N.b。如果省略CASE语句的ELSE [Subject]部分,而不是忽略UPDATE,它将字段设置为NULL。

答案 1 :(得分:6)

update Message set
    Subject = (case when @SubjectChanged = 1 then @Subject else Subject end),
    Body = (case when @BodyChanged = 1 then @Body else Body end)

where MessageID = @MessageID

这应该就是你所需要的。但是,如果你真的无法更新字段(如果它没有更改),那么你必须在单独的语句中进行。

if @SubjectChanged = 1 
    update Message set Subject = @Subject where MessageID = @MessageID
if @BodyChanged = 1 
    update Message set Body = @Body where MessageID = @MessageID

答案 2 :(得分:6)

到目前为止,您最好的选择是使用明确的IF语句:

IF @subjectHasChanged = 1 and @bodyHasChanged = 1
 UPDATE Messages SET Subject = @subject, Body = @body 
   WHERE MessageId = @MessageId
ELSE IF @subjectHasChanged = 1
 UPDATE Messages SET Subject = @subject WHERE MessageId = @MessageId
ELSE IF @bodyHasChanged
 UPDATE Messages SET Body = @body WHERE MessageId = @MessageId

从性能的角度来看,没有什么比这更好的了。因为SQL在查询编译期间可以看到您只更新Body或Subject或两者,它可以生成适当的计划,例如甚至不打开(更新)您在Subject上的非聚集索引(如果您有一个,当然)当你只更新Body。

从代码质量的角度来看,这是灾难,维持的噩梦。但承认问题是80%解决问题:)。例如,您可以使用代码生成技术来维护此类问题过程。

另一种可行的方法实际上是使用动态SQL,在过程中构造UPDATE并使用sp_executesql。它有自己的一组问题,就像所有动态SQL一样。有关于动态SQL问题的资源,有解决方法和解决方案,请参阅The Curse and Blessings of Dynamic SQL

答案 3 :(得分:1)

对我而言,就像你在浪费了很多精力。如果检索六个值,则将它们显示给用户(在某些用户界面中),并且可以更改它们的一些可变数量 并点击“保存”按钮 - 然后每次只更新所有6个字段,从用户输入字段中获取新值。

有些人可能没有改变,但那是什么。这样的代码更简单。

答案 4 :(得分:0)

对存储过程参数使用DEFAULT值。

create proc UpdateMessage(
  @MessageID int,  -- mandatory
  @Subject nvarchar(100) = NULL, 
  @Body nvarchar(max) = NULL)

然后,您可以通过以下方式构建更新:

Update [Message] 
SET 
[Subject] = ISNULL(@Subject, [Subject]),
Body = ISNULL(@Body, Body)
WHERE MessageID = @MessageID

答案 5 :(得分:0)

CREATE PROCEDURE UpdateMessage
  @MessageID int, 
  @Subject nvarchar(100), 
  @Body nvarchar(max),
AS
BEGIN
    if(@Subject is null or @Subject='')
        SELECT @Subject=Subject FROM Message WHERE MessageID=@MessageID
    if(@Body is null or @Body='')
        SELECT @Body=Body FROM Message WHERE MessageID=@MessageID
    UPDATE Message SET Subject=@Subject, Body=@Body WHERE MessageID=@MessageID
END
GO

答案 6 :(得分:0)

我不确定这是否是最好的方法,但也许你可以试试

IF @SubjectChanged = 1 THEN
   BEGIN
      UPDATE [Message]
      SET [Subject] = @Subject
      WHERE MessageID = @MessageID     
   END
END

IF @BodyChanged = 1 THEN
   BEGIN
      UPDATE [Message]
      SET Body = @Body
      WHERE MessageID = @MessageID
   END
END

答案 7 :(得分:0)

如果您要求将其存储在单个存储过程中,我强烈建议您使用Adam Robinson的方法。

更好的方法是简单地为每个更新使用单独的存储过程。