T-SQL 条件更新 (v2)

发布于 2024-08-07 12:50:18 字数 764 浏览 7 评论 0原文

我有一个表:

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 个参数可以更新,而不是两个)

I have a table:

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

After a message is being updated on UI, I call a stored proc to update that table. In some cases user might update just subject, in other cases just body. I want this stored proc to only update what has changed, so I'm also passing flags showing whether subject or body has been updated:

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

And now i'm confused how to build the conditional UPDATE statement. My first thought was to use 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

... but that doesn't seem to be a correct syntax as CASE has to be the right side of an assigment.

Any ideas how I could do that? (And keep in mind that in reality there are 6 parameters that can be updated, not two)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

回梦 2024-08-14 12:50:18

创建语句所需的语法是:

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,而是将该字段设置为 NULL。

The syntax required to create your statement is:

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 you still want to stick to it after all the suggestions.

N.b. if you leave out the ELSE [Subject] part of the CASE statements, instead of ignoring the UPDATE it sets the field to NULL.

給妳壹絲溫柔 2024-08-14 12:50:18

到目前为止,最好的选择是使用显式 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 或主题或两者,所以它可以生成适当的计划,例如甚至不必费心打开(用于更新)主题上的非聚集索引(如果您有当然是一)当你只更新 Body 时。

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

另一种可行的方法实际上是使用动态 SQL,在过程中构造 UPDATE 并使用 sp_executesql。与所有动态 SQL 一样,它也有自己的一系列问题。有一些关于动态 SQL 问题的资源,以及解决方法和解决方案,请参阅动态 SQL 的诅咒和祝福。

Your best bet, by far, is to use explicit IF statements:

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

From a performance point of view, nothing beats this. Because SQL can see during query compilation that you only update Body, or Subject, or both, it can generate the appropriate plan, for instance not even bothering to open (for update) the non-clustered index you have on Subject (if you have one, of course) when you only update Body.

From a code code quality point of view, this is disaster, a nightmare to maintain. But acknowledging the problem is 80% solving the problem :) . You can use code generation techniques for instance to maintain such problem procedures.

Another viable approach is actually to use dynamic SQL, construct the UPDATE in the procedure and use sp_executesql. It has its own set of problems, as all dynamic SQL has. There are resources about dynamic SQL problems, and there are workarounds and solutions, see The Curse and Blessings of Dynamic SQL.

南冥有猫 2024-08-14 12:50:18
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
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

That should really be all you need. However, if you truly can't update the field if it hasn't changed, then you'll have to do it in separate statements.

if @SubjectChanged = 1 
    update Message set Subject = @Subject where MessageID = @MessageID
if @BodyChanged = 1 
    update Message set Body = @Body where MessageID = @MessageID
多情癖 2024-08-14 12:50:18

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

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

Seems to me like you are wasting a lot of effort. If you retrieve the six values, display them to the user (in some user interface) and they can change some variable number of them
and hit a "save" button - then just update all 6 fields every time, getting the new values from the user input fields.

Some may not have changed, but so what. Much simpler code that way.

做个少女永远怀春 2024-08-14 12:50:18

使用存储过程参数的 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

Use DEFAULT values for the stored procedure parameters.

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

Then, you can structure your update in this way:

Update [Message] 
SET 
[Subject] = ISNULL(@Subject, [Subject]),
Body = ISNULL(@Body, Body)
WHERE MessageID = @MessageID
放手` 2024-08-14 12:50:18
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
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
夜灵血窟げ 2024-08-14 12:50:18

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

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

I am not sure if this is the best way to do it, but maybe you can try

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
难得心□动 2024-08-14 12:50:18

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

更好的方法是简单地对每一项更新使用单独的存储过程。

I would highly recommend using Adam Robinson's method if you require this to be in a single stored procedure.

Even better would be to simply use separate stored procedures for each one of these updates.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文