2009-10-10 8 views
12

Tengo una tabla:T-SQL ACTUALIZACIÓN condicional (v2)

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

Después de un mensaje está siendo actualizado en la interfaz de usuario, que llamo un procedimiento almacenado para actualizar esa tabla. En algunos casos, el usuario puede actualizar solo el tema, en otros casos solo cuerpo. Quiero que este procedimiento almacenado sólo para actualizar lo que ha cambiado, así que también estoy pasando símbolos que indican que si la materia o cuerpo se ha actualizado:

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

y ahora estoy confundido cómo construir el UPDATE sentencia condicional. Lo primero que pensé fue utilizar 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 

... pero eso no parece ser una sintaxis correcta como CASE tiene que ser el lado derecho de una assigment.

¿Alguna idea de cómo podría hacer eso? (Y tenga en cuenta que en realidad hay 6 parámetros que se pueden actualizar, no dos)

Respuesta

24

La sintaxis necesaria para crear su estado de cuenta es:

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 

si todavía quiere ser fiel a ella después de que todas las sugerencias.

N.b. si omite la parte ELSE [Subject] de las sentencias CASE, en lugar de ignorar la ACTUALIZACIÓN, establece el campo en NULL.

1

Me parece que está perdiendo un montón de esfuerzo. Si recupera los seis valores, muéstrelos al usuario (en alguna interfaz de usuario) y ellos pueden cambiar un número variable de ellos y presionar un botón "guardar" - luego simplemente actualice los 6 campos cada vez, obteniendo los nuevos valores de los campos de entrada del usuario.

Es posible que algunos no hayan cambiado, pero ¿y qué? Código mucho más simple de esa manera.

+0

Tenga en cuenta que el cuerpo del mensaje puede ser de 5-10-50kb y, si el usuario acaba de actualizar el asunto, es extremadamente inútil enviar el cuerpo del mensaje de vuelta y realizar la actualización del campo cuando no sea necesario. El sitio web tiene mucha carga, por lo que debo asegurarme de que optimizo cuando sea posible. – Andrey

+0

¿Por qué no usar procedimientos almacenados por separado? ¿De verdad se debe incluir en un solo sproc? Es probable que desee hacer algunas modificaciones en el futuro que arruinarán por completo su "increíble plan" –

+1

"¿Por qué no utilizar procedimientos almacenados por separado entonces?" - para guardar en múltiples llamadas a bases de datos – Andrey

0

Use valores DEFAULT para los parámetros del procedimiento almacenado.

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

A continuación, puede estructurar su actualización de esta manera:

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

¿Qué sucede si está tratando de actualizar un campo a NULL? –

+0

Y también, cuando @Body es NULL, estoy actualizando un campo con su propio valor, lo que está desperdiciando recursos valiosos (por ejemplo, ¿qué pasa si Message.Body es de 1 Meg? Es una actualización bastante pesada, e innecesaria). – 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 

que realmente debería ser todo lo que necesita. Sin embargo, si realmente no puede actualizar el campo si no ha cambiado, entonces tendrá que hacerlo en declaraciones separadas.

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

No estoy seguro si esto es la mejor manera de hacerlo, pero tal vez pueda intentar

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

Su mejor apuesta, por lejos, es el uso explícito si las declaraciones:

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 

Desde el punto de vista del rendimiento, nada supera esto. Como SQL puede ver durante la compilación de consultas que solo actualiza Body o Subject, o ambos, puede generar el plan adecuado, por ejemplo, sin molestarse en abrir (para actualizar) el índice no agrupado que tiene en Subject (si tiene uno, por supuesto) cuando solo actualiza Body.

Desde el punto de vista de la calidad del código de código, esto es un desastre, una pesadilla para mantener. Pero reconocer el problema es 80% resolviendo el problema :). Puede usar técnicas de generación de código, por ejemplo, para mantener dichos procedimientos de problemas.

Otro enfoque viable es en realidad usar SQL dinámico, construir la ACTUALIZACIÓN en el procedimiento y usar sp_executesql.Tiene su propio conjunto de problemas, como todos los SQL dinámicos. Hay recursos sobre problemas de SQL dinámico, y hay soluciones y soluciones alternativas, consulte The Curse and Blessings of Dynamic SQL.

0

Recomiendo encarecidamente utilizar el método de Adam Robinson si requiere que esté en un solo procedimiento almacenado.

Aún mejor sería simplemente utilizar procedimientos almacenados separados para cada una de estas actualizaciones.