Actualice varias filas en la misma consulta usando PostgreSQL

192

Estoy buscando actualizar varias filas en PostgreSQL en una declaración. ¿Hay alguna manera de hacer algo como lo siguiente?

UPDATE table 
SET 
 column_a = 1 where column_b = '123',
 column_a = 2 where column_b = '345'
nuevoUserNameHere
fuente
Sigo intentando encontrarlo en esa página, pero no lo consigo. Veo dónde puede actualizar varias filas usando una instrucción where, pero no entiendo cómo actualizar varias filas cada una con su propia instrucción where. También busqué en google y no encontré una respuesta realmente clara, así que esperaba que alguien pudiera proporcionar un ejemplo claro sobre esto.
newUserNameHere
Perdón mi error. Actualizado.
cero323

Respuestas:

427

También puede usar la update ... fromsintaxis y usar una tabla de mapeo. Si desea actualizar más de una columna, es mucho más generalizable:

update test as t set
    column_a = c.column_a
from (values
    ('123', 1),
    ('345', 2)  
) as c(column_b, column_a) 
where c.column_b = t.column_b;

Puede agregar tantas columnas como desee:

update test as t set
    column_a = c.column_a,
    column_c = c.column_c
from (values
    ('123', 1, '---'),
    ('345', 2, '+++')  
) as c(column_b, column_a, column_c) 
where c.column_b = t.column_b;

sql fiddle demo

Roman Pekar
fuente
11
Además, uno puede tener que especificar un tipo de datos correcto. Un ejemplo con una fecha: ... from (values ('2014-07-21'::timestamp, 1), ('2014-07-20', 2), ...Más detalles en la documentación de PostgreSQL
José Andias
Funciona muy bien, gracias por aclarar! La documentación de Postgres para esto es una lectura un poco confusa.
skwidbreth
52

Basado en la solución de @Roman, puede establecer múltiples valores:

update users as u set -- postgres FTW
  email = u2.email,
  first_name = u2.first_name,
  last_name = u2.last_name
from (values
  (1, '[email protected]', 'Hollis', 'O\'Connell'),
  (2, '[email protected]', 'Robert', 'Duncan')
) as u2(id, email, first_name, last_name)
where u2.id = u.id;
Benjamin Crouzier
fuente
44
Esta parece ser su solución ... ACTUALIZAR DESDE (VALORES ...) DONDE. ¿Cómo se basa solo?
Evan Carroll
14
Prefiero esta respuesta porque los nombres de las variables facilitan la comprensión de lo que está sucediendo.
Jon Lemmon
Guau. Preciso y claro. Estoy tratando de implementar algo como esto en GoLang. Entonces, ¿puedo pasar una matriz de estructuras en lugar de valores? Algo como esto, from (values $1)donde $ 1 es una matriz de estructuras. En el caso anterior, el estricto tendría id, first_name y last_name como propiedades.
Reshma Suresh
26

Sí tu puedes:

UPDATE foobar SET column_a = CASE
   WHEN column_b = '123' THEN 1
   WHEN column_b = '345' THEN 2
END
WHERE column_b IN ('123','345')

Y prueba de funcionamiento: http://sqlfiddle.com/#!2/97c7ea/1

cero323
fuente
8
Esto está mal ... Va a actualizar todas las filas, incluso si no es '123'ni '345'. Deberías usar WHERE column_b IN ('123','456')...
MatheusOl
1
Creo que se '456'supone que es'345'
Roman Pekar
2
Si agrega ELSE column_bdespués de la última WHEN ? THEN ?línea, la columna se establecerá en su valor actual, evitando así lo que MatheusQI dijo que sucedería.
Kevin Orriss
1
Eso no es lo que pidió ... necesita actualizar varios cols, no configurar el col A basado en el col B.
Amalgovinus
¿No es exactamente lo que solicitó OP: solo column_a necesita actualización (en función del valor de column_b), no varias columnas, ¿verdad?
kevlarr
3

Encontré un escenario similar y la expresión CASE me fue útil.

UPDATE reports SET is_default = 
case 
 when report_id = 123 then true
 when report_id != 123 then false
end
WHERE account_id = 321;

Informes: aquí hay una tabla, account_id es igual para los report_ids mencionados anteriormente. La consulta anterior establecerá 1 registro (el que coincide con la condición) en verdadero y todos los que no coinciden en falso.

Chico ricky
fuente
2

Para actualizar varias filas en una sola consulta, puede intentar esto

UPDATE table_name
SET 
column_1 = CASE WHEN any_column = value and any_column = value THEN column_1_value end,
column_2 = CASE WHEN any_column = value and any_column = value THEN column_2_value end,
column_3 = CASE WHEN any_column = value and any_column = value THEN column_3_value end,
.
.
.
column_n = CASE WHEN any_column = value and any_column = value THEN column_n_value end

Si no necesita una condición adicional, elimine andparte de esta consulta

Omar
fuente
0

Supongamos que tiene una matriz de ID y una matriz equivalente de estados : aquí hay un ejemplo de cómo hacer esto con un SQL estático (una consulta sql que no cambia debido a diferentes valores) de las matrices:

drop table if exists results_dummy;
create table results_dummy (id int, status text, created_at timestamp default now(), updated_at timestamp default now());
-- populate table with dummy rows
insert into results_dummy
(id, status)
select unnest(array[1,2,3,4,5]::int[]) as id, unnest(array['a','b','c','d','e']::text[]) as status;

select * from results_dummy;

-- THE update of multiple rows with/by different values
update results_dummy as rd
set    status=new.status, updated_at=now()
from (select unnest(array[1,2,5]::int[]) as id,unnest(array['a`','b`','e`']::text[]) as status) as new
where rd.id=new.id;

select * from results_dummy;

-- in code using **IDs** as first bind variable and **statuses** as the second bind variable:
update results_dummy as rd
set    status=new.status, updated_at=now()
from (select unnest(:1::int[]) as id,unnest(:2::text[]) as status) as new
where rd.id=new.id;
Tal Barda
fuente