我有一张桌子:
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个参数可以更新,而不是两个)
答案 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的方法。
更好的方法是简单地为每个更新使用单独的存储过程。