T-SQL условное ОБНОВЛЕНИЕ (v2) - PullRequest
14 голосов
/ 10 октября 2009

У меня есть таблица:

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

После обновления сообщения в пользовательском интерфейсе я вызываю сохраненный процесс для обновления этой таблицы. В некоторых случаях пользователь может обновить только тему, в других случаях только тело. Я хочу, чтобы этот сохраненный процесс обновлял только то, что изменилось, поэтому я также передаю флаги, показывающие, был ли обновлен предмет или текст:

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 должен быть правой стороной задания.

Есть идеи, как я мог это сделать? (И имейте в виду, что в действительности существует шесть параметров, которые можно обновить, а не два)

Ответы [ 8 ]

27 голосов
/ 12 октября 2009

Синтаксис, необходимый для создания вашего заявления:

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. если вы пропустите часть ELSE [Subject] операторов CASE, вместо игнорирования UPDATE он устанавливает в поле значение NULL.

7 голосов
/ 10 октября 2009

Лучше всего использовать явные операторы 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, и есть обходные пути и решения, см. Проклятие и благословения динамического SQL .

6 голосов
/ 10 октября 2009
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
1 голос
/ 10 октября 2009

Мне кажется, что ты тратишь много сил. Если вы получите шесть значений, отобразите их пользователю (в каком-то пользовательском интерфейсе), и они могут изменить некоторое их переменное число. и нажмите кнопку «Сохранить» - затем просто обновляйте все 6 полей каждый раз, получая новые значения из полей ввода пользователя.

Некоторые, возможно, не изменились, но что с того. Так проще код.

0 голосов
/ 10 октября 2009

Я бы настоятельно рекомендовал использовать метод Адама Робинсона, если вам требуется, чтобы это было в одной хранимой процедуре.

Еще лучше было бы просто использовать отдельные хранимые процедуры для каждого из этих обновлений.

0 голосов
/ 10 октября 2009

Я не уверен, что это лучший способ сделать это, но, может быть, вы можете попробовать

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
0 голосов
/ 10 октября 2009
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
0 голосов
/ 10 октября 2009

Используйте значения 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
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...