代码之家  ›  专栏  ›  技术社区  ›  Andrey

T-SQL条件更新(v2)

  •  14
  • Andrey  · 技术社区  · 15 年前

    我有一张桌子:

    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
    

    …但这似乎不是一个正确的语法 案例 必须是分配的右侧。

    我能怎么做吗?(请记住,实际上有6个参数可以更新,而不是两个)

    8 回复  |  直到 7 年前
        1
  •  27
  •   Ralph Lavelle    15 年前

    创建语句所需的语法是:

    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
    

    如果你还是想坚持到底,在所有的建议之后。

    注意:如果省略case语句的else[subject]部分,而不是忽略update,它会将字段设置为空。

        2
  •  6
  •   Adam Robinson    15 年前
    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
    
        3
  •  6
  •   Remus Rusanu    15 年前

    到目前为止,最好的办法是使用显式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可以在查询编译期间看到您只更新主体或主体,或者两者都更新,所以它可以生成适当的计划,例如,当您只更新主体时,它甚至不需要打开(用于更新)针对主体的非聚集索引(当然,如果您有一个)。

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

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

        4
  •  1
  •   Tim Cooper    13 年前

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

    有些可能没有改变,但那又怎样呢?这样的代码就简单多了。

        5
  •  0
  •   Rodrigo    15 年前

    对存储过程参数使用默认值。

    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
    
        6
  •  0
  •   dreadwail    15 年前
    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
    
        7
  •  0
  •   Waleed Al-Balooshi    15 年前

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

    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
    
        8
  •  0
  •   Joe Phillips    15 年前

    如果您需要在单个存储过程中使用AdamRobinson方法,我强烈建议您使用该方法。

    更好的方法是对这些更新中的每一个使用单独的存储过程。