2009-10-10 10 views
12

Ho una tabella:T-SQL UPDATE condizionale (v2)

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

Dopo che un messaggio è in fase di aggiornamento sulla UI, che io chiamo un proc memorizzato per aggiornare quel tavolo. In alcuni casi l'utente può aggiornare solo il soggetto, in altri casi solo il corpo. Voglio che questo proc memorizzato per aggiornare solo ciò che è cambiato, quindi sto passando anche bandiere che mostrano se nell'oggetto o nel corpo è stato aggiornato:

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

e ora sono confuso come costruire il condizionale UPDATE dichiarazione. Il mio primo pensiero è stato quello di utilizzare 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 

... ma questo non sembra essere una sintassi corretta come CASE deve essere sul lato destro di un assigment.

Qualche idea su come potrei farlo? (E tieni presente che in realtà ci sono 6 parametri che possono essere aggiornati, non due)

risposta

24

La sintassi necessaria per creare la vostra dichiarazione è:

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 

se si vuole ancora di attenersi ad esso, dopo tutti i suggerimenti.

N.b. se si omette la parte ELSE [Subject] delle istruzioni CASE, invece di ignorare UPDATE, imposta il campo su NULL.

1

Mi sembra che tu stia sprecando un sacco di sforzi. Se si recuperano i sei valori, visualizzarli all'utente (in alcune interfacce utente) e possono modificarne alcuni numeri variabili e premere un pulsante "Salva", quindi aggiornare semplicemente tutti i 6 campi ogni volta, ottenendo i nuovi valori da i campi di input dell'utente.

Alcuni potrebbero non essere stati modificati, ma allora cosa. Codice molto più semplice in questo modo.

+0

Ricordare che il corpo del messaggio può essere 5-10-50kb e se un utente ha appena aggiornato l'oggetto, è estremamente dispendioso rimandare il corpo del messaggio e anche eseguire l'aggiornamento di quel campo al db quando non è necessario. Il sito web è pesantemente carico, quindi ho bisogno di assicurarmi di ottimizzare dove possibile. – Andrey

+0

Perché non utilizzare semplicemente stored procedure separate? Ha davvero bisogno di essere racchiuso in un singolo sproc? Probabilmente vorrai apportare alcune modifiche in futuro che rovineranno completamente il tuo "piano fantastico" –

+1

"Perché non usare solo stored procedure separate allora?" - per salvare su più chiamate db – Andrey

0

Utilizzare i valori DEFAULT per i parametri di procedura memorizzati.

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

Quindi, è possibile strutturare l'aggiornamento in questo modo:

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

Cosa succede se si sta tentando di aggiornare un campo su NULL? –

+0

Inoltre, quando @Body è NULL, sto aggiornando un campo con il suo valore, che sta sprecando risorse preziose (ad esempio, se Message.Body è 1 Meg? È un aggiornamento piuttosto pesante e non necessario). – Andrey

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 

che in realtà dovrebbe essere tutto il necessario. Tuttavia, se lo non è veramente in grado di aggiornare il campo se non è stato modificato, sarà necessario farlo in istruzioni separate.

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

Non sono sicuro se questo è il modo migliore per farlo, ma forse si può provare

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 
6

La cosa migliore, di gran lunga, è quello di utilizzare esplicite dichiarazioni:

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 

Dal punto di vista delle prestazioni, non c'è niente di meglio di questo. Poiché SQL può vedere durante la compilazione delle query che si aggiorna solo Corpo, o Oggetto, o entrambi, può generare il piano appropriato, ad esempio non si preoccupa nemmeno di aprire (per aggiornare) l'indice non cluster che si ha sul soggetto (se si ha uno, ovviamente) quando aggiorni solo Body.

Dal punto di vista della qualità del codice, questo è un disastro, un incubo da mantenere. Ma riconoscere il problema è risolvere l'80% del problema :). È possibile utilizzare tecniche di generazione del codice, ad esempio, per mantenere tali procedure problematiche.

Un altro approccio praticabile consiste nell'utilizzare SQL dinamico, creare l'UPDATE nella procedura e utilizzare sp_executesql.Ha una serie di problemi, come tutti gli SQL dinamici. Esistono risorse sui problemi dinamici di SQL e sono disponibili soluzioni e soluzioni, vedere The Curse and Blessings of Dynamic SQL.

0

Si consiglia vivamente di utilizzare il metodo di Adam Robinson se si richiede che questo sia in una singola stored procedure.

Ancora meglio sarebbe semplicemente utilizzare stored procedure separate per ciascuno di questi aggiornamenti.