Mettre à jour plusieurs lignes dans la même requête à l'aide de PostgreSQL

192

Je cherche à mettre à jour plusieurs lignes dans PostgreSQL dans une seule déclaration. Existe-t-il un moyen de faire quelque chose comme ce qui suit?

UPDATE table 
SET 
 column_a = 1 where column_b = '123',
 column_a = 2 where column_b = '345'
newUserNameHere
la source
J'essaye de le trouver sur cette page mais je ne peux pas l'obtenir. Je vois où vous pouvez mettre à jour plusieurs lignes en utilisant une instruction where, mais je ne comprends pas comment mettre à jour plusieurs lignes chacune avec sa propre instruction where. J'ai également cherché sur Google et je n'ai pas trouvé de réponse claire, j'espérais donc que quelqu'un pourrait fournir un exemple clair à ce sujet.
newUserNameHere
Désolé mon erreur. Actualisé.
zero323

Réponses:

427

Vous pouvez également utiliser la update ... fromsyntaxe et utiliser une table de mappage. Si vous souhaitez mettre à jour plus d'une colonne, c'est beaucoup plus généralisable:

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;

Vous pouvez ajouter autant de colonnes que vous le souhaitez:

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
la source
11
En outre, il peut être nécessaire de spécifier un type de données correct. Un exemple avec une date: ... from (values ('2014-07-21'::timestamp, 1), ('2014-07-20', 2), ...Plus de détails dans la documentation PostgreSQL
José Andias
Fonctionne très bien, merci de clarifier! La documentation Postgres pour cela rend la lecture un peu confuse.
skwidbreth
52

Sur la base de la solution de @Roman, vous pouvez définir plusieurs valeurs:

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
la source
4
Cela semble être sa solution .. MISE À JOUR DE (VALEURS ...) O. Comment est-ce uniquement basé?
Evan Carroll
14
Je préfère cette réponse car les noms des variables permettent de comprendre plus facilement ce qui se passe.
Jon Lemmon
Sensationnel. Précis et clair. J'essaye d'implémenter quelque chose comme ça dans GoLang. Alors, puis-je passer un tableau de structures en place pour les valeurs? Quelque chose comme ça, from (values $1)Où $ 1 est un tableau de structures. Dans le cas ci-dessus, le strict aurait comme propriétés id, first_name et last_name.
Reshma Suresh
26

Oui, vous pouvez:

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

Et preuve de travail: http://sqlfiddle.com/#!2/97c7ea/1

zéro323
la source
8
C'est faux ... Vous mettrez à jour toutes les lignes, même si ce n'est pas '123'ni '345'. Vous devriez utiliser WHERE column_b IN ('123','456')...
MatheusOl
1
je pense '456'censé être'345'
Roman Pekar
2
Si vous ajoutez ELSE column_baprès la dernière WHEN ? THEN ?ligne, la colonne sera définie sur sa valeur actuelle, empêchant ainsi ce que MatheusQI a dit de se produire.
Kevin Orriss
1
Ce n'est pas ce qu'il a demandé ... il a besoin de mettre à jour plusieurs cols, pas de définir le col A basé sur le col B.
Amalgovinus
N'est-ce pas exactement ce que OP a demandé - seule colonne_a a besoin d'être mise à jour (en fonction de la valeur de colonne_b), pas plusieurs colonnes, non?
kevlarr le
3

Je suis tombé sur un scénario similaire et l'expression CASE m'a été utile.

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

Rapports - est un tableau ici, account_id est le même pour les report_ids mentionnés ci-dessus. La requête ci-dessus définira 1 enregistrement (celui qui correspond à la condition) sur true et tous les enregistrements non correspondants sur false.

Ricky Boy
la source
2

Pour mettre à jour plusieurs lignes dans une seule requête, vous pouvez essayer ceci

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 vous n'avez pas besoin de condition supplémentaire, supprimez une andpartie de cette requête

Omar
la source
0

Supposons que vous ayez un tableau d' ID et un tableau équivalent de statuts - voici un exemple comment faire cela avec un SQL statique (une requête SQL qui ne change pas en raison de valeurs différentes) des tableaux:

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
la source