Contenido de esta guía
La idea: una fila puede recibir un valor distinto dentro del mismo UPDATE
Un UPDATE normal suele asignar el mismo valor o la misma fórmula a todas las filas que cumplen WHERE. Pero a veces ya conoces un valor diferente para cada fila: por ejemplo, después de un recuento de inventario quieres dejar el producto 1 con 20 unidades, el 2 con 9 y el 3 con 6.
En ese caso, una expresión CASE puede decidir qué valor recibe cada fila. El modelo mental es: WHERE limita qué filas pueden cambiar; CASE decide el valor nuevo dentro de ese conjunto. Las dos partes son necesarias.
Este patrón resulta útil para lotes pequeños y explícitos. Si los valores nuevos ya están en otra tabla o el lote crece mucho, suele ser más claro usar una tabla de origen y el patrón UPDATE con JOIN / UPDATE FROM.
Patrón general
UPDATE tabla
SET columna = CASE clave
WHEN valor_clave_1 THEN nuevo_valor_1
WHEN valor_clave_2 THEN nuevo_valor_2
ELSE columna
END
WHERE clave IN (valor_clave_1, valor_clave_2);La forma simple CASE clave WHEN ... funciona bien cuando comparas la misma columna con varios valores concretos. Si cada rama necesita una condición distinta, puedes usar CASE WHEN condicion THEN valor ... END.
ELSE columna conserva el valor actual cuando ninguna rama coincide, pero no sustituye al WHERE. Mantener ambos hace visible la intención: el WHERE restringe el alcance y el ELSE evita convertir una fila inesperada en NULL si la expresión cambia más adelante.
Ejemplo comprobado: corregir tres existencias
En la base educativa, Ratón tiene 18 unidades, Teclado 7 y Monitor 24 tiene 4. Supón que un recuento físico confirma 20, 9 y 6 unidades respectivamente. Primero conviene revisar las filas objetivo; después puedes ejecutar el cambio dentro de una transacción y comprobar el resultado antes de decidir si confirmarlo.
BEGIN;
SELECT id, nombre, stock
FROM productos
WHERE id IN (1, 2, 3)
ORDER BY id;
UPDATE productos
SET stock = CASE id
WHEN 1 THEN 20
WHEN 2 THEN 9
WHEN 3 THEN 6
ELSE stock
END
WHERE id IN (1, 2, 3);
SELECT id, nombre, stock
FROM productos
WHERE id IN (1, 2, 3)
ORDER BY id;
ROLLBACK;| id | nombre | stock |
|---|---|---|
| 1 | Ratón | 20 |
| 2 | Teclado | 9 |
| 3 | Monitor 24 | 6 |
El UPDATE afecta exactamente tres filas. Como el ejemplo termina con ROLLBACK, la base vuelve a 18, 7 y 4 unidades. La transacción permite revisar el estado resultante, pero la seguridad principal sigue estando en una condición que identifica las filas correctas.
CASE simple o CASE buscado
Cuando cada fila se identifica por una clave
Usa la forma simple si todas las ramas comparan la misma expresión:
SET stock = CASE id
WHEN 1 THEN 20
WHEN 2 THEN 9
WHEN 3 THEN 6
ELSE stock
ENDCuando cada rama tiene una condición diferente
La forma buscada permite condiciones más expresivas. Por ejemplo, podrías aplicar distintos cálculos por nivel de stock:
UPDATE productos
SET stock = CASE
WHEN stock = 0 THEN 5
WHEN stock < 5 THEN stock + 3
ELSE stock
END
WHERE stock < 5;En ambos casos, CASE devuelve una expresión de resultado. No es un bucle ni ejecuta varios UPDATE; el motor evalúa la expresión para cada fila dentro del alcance de la sentencia.
Tres controles que evitan cambios masivos
1. Probar primero el mismo WHERE con SELECT
Antes de modificar, ejecuta un SELECT con la misma condición. Si esperas tres filas y aparecen treinta, el problema está en el alcance y todavía estás a tiempo de corregirlo.
2. No confiar solo en ELSE
Un ELSE stock puede dejar sin cambios las filas que no coinciden con ninguna rama, pero un UPDATE sin WHERE sigue evaluando todas las filas de la tabla. Eso aumenta trabajo, complica el razonamiento y puede afectar otras columnas si la sentencia evoluciona.
3. Evitar CASE sin ELSE cuando existe una columna NOT NULL
Si ninguna rama coincide y no existe ELSE, CASE devuelve NULL. En una columna que no acepta nulos, eso puede provocar un error; en una columna que sí los acepta, puede introducir un dato que no pretendías.
Cuándo no conviene un CASE enorme
Un CASE de tres, cinco o diez claves puede ser fácil de auditar. Cientos de ramas ya esconden la relación entre clave y valor dentro de una sentencia difícil de revisar. Cuando los valores nuevos provienen de otro conjunto de datos, suele ser mejor representarlos como filas y relacionarlos con la tabla objetivo.
Según el motor, eso puede expresarse con UPDATE ... FROM, UPDATE ... JOIN, una tabla temporal, una CTE o una estrategia equivalente. La página UPDATE con JOIN / UPDATE FROM explica esas diferencias. No hay una regla universal de rendimiento: la elección depende del tamaño del lote, índices, plan y motor.
Compatibilidad entre PostgreSQL, MySQL, SQL Server y SQLite
El patrón básico es ampliamente portable porque UPDATE ... SET acepta expresiones y CASE es una expresión condicional en estos motores. PostgreSQL permite expresiones en los valores de SET y documenta CASE como expresión condicional; MySQL define los valores de una asignación de UPDATE como expresiones y admite el operador CASE; SQL Server documenta expresamente el uso de CASE dentro de UPDATE; SQLite permite expresiones escalares a la derecha de cada asignación y define ambas formas de CASE.
Lo que sí cambia entre motores es la sintaxis de transacciones, las extensiones de UPDATE y las formas de actualizar a partir de otras tablas. Para este patrón pequeño, la estructura UPDATE ... SET columna = CASE ... END WHERE ... evita depender de esas extensiones.
Comprueba lo aprendido
Cambia dos estados con valores diferentes
En la tabla pedidos, cambia el pedido 102 a 'enviado' y el 105 a 'cancelado' en una sola sentencia. No modifiques ninguna otra fila.
Criterio de éxito: después del UPDATE, la consulta de comprobación devuelve exactamente 102 → enviado y 105 → cancelado.
CASE id porque ambas ramas comparan la misma clave.ELSE estado para conservar el valor si una fila no coincide con las ramas.WHERE id IN (102, 105).Solución razonada
UPDATE pedidos
SET estado = CASE id
WHEN 102 THEN 'enviado'
WHEN 105 THEN 'cancelado'
ELSE estado
END
WHERE id IN (102, 105);
SELECT id, estado
FROM pedidos
WHERE id IN (102, 105)
ORDER BY id;| id | estado |
|---|---|
| 102 | enviado |
| 105 | cancelado |
CASE asigna un estado distinto según el identificador; WHERE garantiza que solo esos dos pedidos entren en el alcance. En un entorno real, revisa el SELECT previo y usa una transacción si necesitas comprobar antes de confirmar.
Qué debes recordar
WHEREdecide qué filas pueden cambiar;CASEdecide qué valor recibe cada una.- La forma
CASE clave WHEN ...es adecuada para asignar valores explícitos por identificador. ELSE columnaconserva el valor actual, pero no reemplaza una condición de alcance precisa.- Sin
ELSE, una fila que no coincida con ninguna rama recibeNULL. - Antes de ejecutar, comprueba el mismo
WHEREconSELECTy verifica cuántas filas esperas modificar. - Para lotes grandes o valores almacenados en otra tabla, considera
UPDATE FROM,UPDATE JOINo una estrategia equivalente de tu motor.