Atualize várias linhas na mesma consulta usando o PostgreSQL

190

Eu estou olhando para atualizar várias linhas no PostgreSQL em uma instrução. Existe uma maneira de fazer algo como o seguinte?

UPDATE table 
SET 
 column_a = 1 where column_b = '123',
 column_a = 2 where column_b = '345'
newUserNameHere
fonte
Continuo tentando encontrá-lo nessa página, mas não consigo. Vejo onde você pode atualizar várias linhas usando uma instrução where, mas não entendo como atualizar várias linhas, cada uma com sua própria declaração where. Também pesquisei no google e não encontrei uma resposta clara e clara, por isso esperava que alguém desse um exemplo claro disso.
newUserNameHere
Desculpe meu erro. Atualizada.
precisa saber é o seguinte

Respostas:

423

Você também pode usar update ... fromsintaxe e usar uma tabela de mapeamento. Se você deseja atualizar mais de uma coluna, é muito mais generalizável:

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;

Você pode adicionar quantas colunas desejar:

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
fonte
10
Além disso, pode ser necessário especificar um tipo de dados correto. Um exemplo com uma data: ... from (values ('2014-07-21'::timestamp, 1), ('2014-07-20', 2), ...Mais detalhes na documentação
José Andias
Funciona muito bem, obrigado por esclarecer! A documentação do Postgres para isso torna uma leitura um pouco confusa.
Skwidbreth
52

Com base na solução do @Roman, você pode definir vários 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
fonte
4
Esta parece ser a solução dele. ATUALIZAR DE (VALORES ...) ONDE. Como é baseado apenas?
Evan Carroll
14
Prefiro esta resposta porque os nomes das variáveis ​​facilitam a compreensão do que está acontecendo.
Jon Lemmon
Uau. Preciso e claro. Estou tentando implementar algo assim no GoLang. Então, posso passar uma matriz de estruturas para valores? Algo assim,from (values $1) Onde $ 1 é uma matriz de estruturas. No caso acima, o strict teria id, first_name e last_name como propriedades.
Reshma Suresh 20/06
26

Sim você pode:

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')

E prova de trabalho: http://sqlfiddle.com/#!2/97c7ea/1

zero323
fonte
8
Isso está errado ... Você atualizará todas as linhas, mesmo que não seja '123'nem '345'. Você deve usar WHERE column_b IN ('123','456')...
MatheusOl
1
Eu acho que '456'deveria ser'345'
Roman Pekar 14/13
2
Se você adicionar ELSE column_bapós a última WHEN ? THEN ?linha, a coluna será definida como seu valor atual, impedindo o que o MatheusQI disse que aconteceria.
Kevin Orriss
1
Isso não é o que ele pediu .. ele precisa atualizar vários cols, não set col A baseado em col B.
Amalgovinus
Não é exatamente o que o OP pediu - apenas column_a precisa ser atualizada (com base no valor de column_b), não várias colunas, certo?
kevlarr 27/06
3

Me deparei com um cenário semelhante e a expressão CASE foi útil para mim.

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

Relatórios - é uma tabela aqui, account_id é o mesmo para os report_ids mencionados acima. A consulta acima definirá 1 registro (o que corresponde à condição) como true e todos os não correspondentes como false.

Ricky Boy
fonte
2

Para atualizar várias linhas em uma única consulta, você pode tentar isso

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

se você não precisar de condição adicional, remova andparte desta consulta

Omar
fonte
0

Digamos que você tenha uma matriz de IDs e uma matriz equivalente de status - aqui está um exemplo de como fazer isso com um SQL estático (uma consulta sql que não muda devido a valores diferentes) das matrizes:

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
fonte