Aktualisieren Sie mehrere Zeilen in derselben Abfrage mit PostgreSQL

190

Ich möchte mehrere Zeilen in PostgreSQL in einer Anweisung aktualisieren. Gibt es eine Möglichkeit, Folgendes zu tun?

UPDATE table 
SET 
 column_a = 1 where column_b = '123',
 column_a = 2 where column_b = '345'
newUserNameHere
quelle
Ich versuche immer wieder, es auf dieser Seite zu finden, aber ich kann es nicht bekommen. Ich sehe, wo Sie mehrere Zeilen mit einer where-Anweisung aktualisieren können, aber ich verstehe nicht, wie Sie mehrere Zeilen mit jeweils einer eigenen where-Anweisung aktualisieren können. Ich suchte auch bei Google und fand keine wirklich klare Antwort, also hoffte ich, dass jemand ein klares Beispiel dafür liefern könnte.
newUserNameHere
Entschuldigung, mein Fehler. Aktualisiert.
Null 323

Antworten:

423

Sie können auch die update ... fromSyntax und eine Zuordnungstabelle verwenden. Wenn Sie mehr als eine Spalte aktualisieren möchten, ist dies viel verallgemeinerbarer:

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;

Sie können beliebig viele Spalten hinzufügen:

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
quelle
10
Außerdem muss möglicherweise ein korrekter Datentyp angegeben werden. Ein Beispiel mit Datum: ... from (values ('2014-07-21'::timestamp, 1), ('2014-07-20', 2), ...Weitere Details in der PostgreSQL-Dokumentation
José Andias
Funktioniert super, danke für die Klarstellung! Die Postgres-Dokumentation hierfür sorgt für eine etwas verwirrende Lektüre.
Skwidbreth
51

Basierend auf der Lösung von @Roman können Sie mehrere Werte festlegen:

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
quelle
4
Dies scheint seine Lösung zu sein. UPDATE FROM (VALUES ...) WHERE. Wie basiert es nur?
Evan Carroll
14
Ich bevorzuge diese Antwort, weil die Variablennamen es einfacher machen zu verstehen, was los ist.
Jon Lemmon
Beeindruckend. Präzise und klar. Ich versuche so etwas in GoLang zu implementieren. Kann ich also ein Array von Strukturen für Werte übergeben? So etwas wie: from (values $1)$ 1 ist ein Array von Strukturen. Im obigen Fall hätte der Strict ID, Vorname und Nachname als Eigenschaften.
Reshma Suresh
26

Ja, du kannst:

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

Und Arbeitsnachweis: http://sqlfiddle.com/#!2/97c7ea/1

null323
quelle
8
Dies ist falsch ... Sie werden alle Zeilen aktualisieren, auch wenn dies nicht der '123'Fall ist '345'. Sie sollten verwenden WHERE column_b IN ('123','456')...
MatheusOl
1
Ich denke, '456'sollte sein'345'
Roman Pekar
2
Wenn Sie ELSE column_bnach der letzten WHEN ? THEN ?Zeile hinzufügen , wird die Spalte auf den aktuellen Wert gesetzt, wodurch verhindert wird, dass das, was MatheusQI gesagt hat, passiert.
Kevin Orriss
1
Das ist nicht das, wonach er gefragt hat. Er muss mehrere
Spalten
Ist es nicht genau das, wonach OP gefragt hat - nur column_a muss aktualisiert werden (basierend auf dem Wert von column_b), nicht mehrere Spalten, oder?
Kevlarr
3

Kam über ein ähnliches Szenario und der CASE-Ausdruck war für mich nützlich.

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

Berichte - ist hier eine Tabelle, account_id ist für die oben genannten report_ids identisch. Bei der obigen Abfrage wird 1 Datensatz (derjenige, der der Bedingung entspricht) auf true und alle nicht übereinstimmenden auf false gesetzt.

Ricky Boy
quelle
2

Sie können dies versuchen, um mehrere Zeilen in einer einzigen Abfrage zu aktualisieren

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

Wenn Sie keine zusätzliche Bedingung benötigen, entfernen Sie einen andTeil dieser Abfrage

Omar
quelle
0

Angenommen, Sie haben ein Array mit IDs und ein gleichwertiges Array mit Status. Hier ist ein Beispiel, wie dies mit einem statischen SQL (einer SQL-Abfrage, die sich aufgrund unterschiedlicher Werte nicht ändert) der Arrays durchgeführt wird:

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
quelle