Come fare un aggiornamento + partecipare a PostgreSQL?


508

Fondamentalmente, voglio fare questo:

update vehicles_vehicle v 
    join shipments_shipment s on v.shipment_id=s.id 
set v.price=s.price_per_vehicle;

Sono abbastanza sicuro che funzionerebbe in MySQL (il mio background), ma non sembra funzionare in Postgres. L'errore che ottengo è:

ERROR:  syntax error at or near "join"
LINE 1: update vehicles_vehicle v join shipments_shipment s on v.shi...
                                  ^

Sicuramente c'è un modo semplice per farlo, ma non riesco a trovare la sintassi corretta. Quindi, come lo scriverei in PostgreSQL?


5
La sintassi di Postgres è diversa: postgresql.org/docs/8.1/static/sql-update.html
Marc B

4
Vehicles_vehicle, shipments_shipment? Questa è un'interessante convenzione sulla denominazione delle tabelle
CodeAndCats

3
@CodeAndCats Haha ... sembra divertente no? Penso che stavo usando Django in quel momento, e i tavoli sono raggruppati per funzionalità. Quindi ci sarebbero state delle vehicles_*tabelle di visualizzazione e alcune shipments_*tabelle.
mpen

Risposte:


777

La sintassi UPDATE è:

[WITH [RECURSIVE] with_query [, ...]]
AGGIORNA [SOLO] tabella [[AS] alias]
    SET {colonna = {espressione | DEFAULT} |
          (colonna [, ...]) = ({espressione | DEFAULT} [, ...])} [, ...]
    [FROM from_list]
    [DOVE condizione | DOVE CORRENTE DI cursore_name]
    [RITORNO * | output_expression [[AS] output_name] [, ...]]

Nel tuo caso penso che tu voglia questo:

UPDATE vehicles_vehicle AS v 
SET price = s.price_per_vehicle
FROM shipments_shipment AS s
WHERE v.shipment_id = s.id 

Se l'aggiornamento si basa su un intero elenco di join di tabella, dovrebbero trovarsi nella sezione AGGIORNAMENTO o nella sezione DA?
ted.strauss,

11
@ ted.strauss: il FROM può contenere un elenco di tabelle.
Mark Byers,

140

Lasciatemi spiegare un po 'di più con il mio esempio.

Compito: informazioni corrette, in cui gli abitanti (gli studenti che stanno per abbandonare la scuola secondaria) hanno presentato domande all'università prima, che hanno ottenuto i certificati scolastici (sì, hanno ottenuto i certificati prima, rispetto a quando sono stati rilasciati (entro la data del certificato specificata). aumentare la data di invio della domanda per adattarla alla data di rilascio del certificato.

Così. prossima affermazione simile a MySQL:

UPDATE applications a
JOIN (
    SELECT ap.id, ab.certificate_issued_at
    FROM abiturients ab
    JOIN applications ap 
    ON ab.id = ap.abiturient_id 
    WHERE ap.documents_taken_at::date < ab.certificate_issued_at
) b
ON a.id = b.id
SET a.documents_taken_at = b.certificate_issued_at;

Diventa simile a PostgreSQL in questo modo

UPDATE applications a
SET documents_taken_at = b.certificate_issued_at         -- we can reference joined table here
FROM abiturients b                                       -- joined table
WHERE 
    a.abiturient_id = b.id AND                           -- JOIN ON clause
    a.documents_taken_at::date < b.certificate_issued_at -- Subquery WHERE

Come puoi vedere, JOINla ONclausola della subquery originale è diventata una delle WHEREcondizioni, che è coniugata da ANDaltre, che sono state spostate dalla subquery senza modifiche. E non è più necessario un JOINtavolo con se stesso (come era in subquery).


16
Come vorresti unirti ad un terzo tavolo?
Growler

19
Basta JOINcome al solito nella FROMlista:FROM abiturients b JOIN addresses c ON c.abiturient_id = b.id
Envek

@Envek - Non puoi usare JOIN lì purtroppo, ho appena controllato. postgresql.org/docs/10/static/sql-update.html
Adrian Smith

3
@AdrianSmith, non puoi usare JOIN in UPDATE stesso, ma puoi usarlo nella from_listclausola UPDATE (che è l'estensione PostgreSQL di SQL). Inoltre, vedere le note sull'unione delle avvertenze sulle tabelle nel collegamento fornito.
Envek,

@Envek - Ah, grazie per il chiarimento, mi sono perso.
Adrian Smith,

130

La risposta di Mark Byers è ottimale in questa situazione. Tuttavia, in situazioni più complesse, è possibile prendere la query di selezione che restituisce righe e valori calcolati e collegarla alla query di aggiornamento in questo modo:

with t as (
  -- Any generic query which returns rowid and corresponding calculated values
  select t1.id as rowid, f(t2, t2) as calculatedvalue
  from table1 as t1
  join table2 as t2 on t2.referenceid = t1.id
)
update table1
set value = t.calculatedvalue
from t
where id = t.rowid

Questo approccio consente di sviluppare e testare la query selezionata e di convertirla in due passaggi nella query di aggiornamento in due passaggi.

Quindi nel tuo caso la query risultante sarà:

with t as (
    select v.id as rowid, s.price_per_vehicle as calculatedvalue
    from vehicles_vehicle v 
    join shipments_shipment s on v.shipment_id = s.id 
)
update vehicles_vehicle
set price = t.calculatedvalue
from t
where id = t.rowid

Nota che gli alias di colonna sono obbligatori, altrimenti PostgreSQL si lamenterà dell'ambiguità dei nomi delle colonne.


1
Mi piace molto questo perché sono sempre un po 'nervoso nel togliere il mio "select" dalla parte superiore e sostituirlo con un "aggiornamento", specialmente con più join. Ciò riduce il numero di dump SQL che dovrei fare prima degli aggiornamenti di massa. :)
dannysauer,

5
Non so perché, ma la versione CTE di questa query è molto più veloce delle soluzioni "plain join" sopra
paul.ago

L'altro vantaggio di questa soluzione è la possibilità di unire da più di due tabelle per raggiungere il valore finale calcolato utilizzando più join nell'istruzione with / select.
Alex Muro,

1
Questo è bellissimo. Avevo realizzato il mio selezionato e, come @dannysauer, avevo paura della conversione. Questo lo fa semplicemente per me. Perfetto!
frostymarvelous,

1
Il tuo primo esempio SQL presenta un errore di sintassi. "update t1" non può usare l'alias dalla sottoquery t, deve usare il nome della tabella: "update table1". Lo fai correttamente nel secondo esempio.
EricS,

80

Per coloro che vogliono effettivamente fare un JOINpuoi anche usare:

UPDATE a
SET price = b_alias.unit_price
FROM      a AS a_alias
LEFT JOIN b AS b_alias ON a_alias.b_fk = b_alias.id
WHERE a_alias.unit_name LIKE 'some_value' 
AND a.id = a_alias.id;

È possibile utilizzare a_alias nella SETsezione a destra del segno di uguale, se necessario. I campi a sinistra del segno di uguale non richiedono un riferimento alla tabella poiché sono considerati dalla tabella "a" originale.


4
Considerando che questa è la prima risposta con un effettivo join (e non all'interno di una subquery), questa dovrebbe essere la vera risposta accettata. Questa o questa domanda dovrebbero essere rinominate per evitare confusione se postgresql supporta i join in aggiornamento o meno.
collana

Funziona. Ma sembra un trucco
M. Habib,

Va notato che secondo la documentazione ( postgresql.org/docs/11/sql-update.html ), elencando la tabella di destinazione nella clausola from si farà auto-unire la tabella di destinazione. Meno fiducioso, mi sembra anche che si tratti di un auto-unione incrociata, che può avere risultati e / o implicazioni non intenzionali.
Ben Collins,

14

Per coloro che desiderano effettuare un JOIN che aggiorna SOLO le righe restituite dal join, utilizzare:

UPDATE a
SET price = b_alias.unit_price
FROM      a AS a_alias
LEFT JOIN b AS b_alias ON a_alias.b_fk = b_alias.id
WHERE a_alias.unit_name LIKE 'some_value' 
AND a.id = a_alias.id
--the below line is critical for updating ONLY joined rows
AND a.pk_id = a_alias.pk_id;

Questo è stato menzionato sopra, ma solo attraverso un commento ... Dal momento che è fondamentale ottenere il risultato corretto pubblicando la NUOVA risposta che funziona


7

Eccoci qui:

update vehicles_vehicle v
set price=s.price_per_vehicle
from shipments_shipment s
where v.shipment_id=s.id;

Semplice come ho potuto farlo. Grazie ragazzi!

Puoi anche fare questo:

-- Doesn't work apparently
update vehicles_vehicle 
set price=s.price_per_vehicle
from vehicles_vehicle v
join shipments_shipment s on v.shipment_id=s.id;

Ma poi hai il tavolo del veicolo due volte e ti è permesso di farlo solo una volta, e non puoi usare l'alias nella parte "set".


@littlegreen Ne sei sicuro? Non lo joinlimita?
Aprire il

4
@mpen Posso confermare che aggiorna tutti i record a un valore. non fa quello che ti aspetteresti.
Adam Bell,

1

Ecco un semplice SQL che aggiorna Mid_Name sulla tabella Name3 utilizzando il campo Middle_Name da Name:

update name3
set mid_name = name.middle_name
from name
where name3.person_id = name.person_id;


0

Nome prima tabella: tbl_table1 (tab1). Nome seconda tabella: tbl_table2 (tab2).

Impostare la colonna ac_status di tbl_table1 su "INATTIVO"

update common.tbl_table1 as tab1
set ac_status= 'INACTIVE' --tbl_table1's "ac_status"
from common.tbl_table2 as tab2
where tab1.ref_id= '1111111' 
and tab2.rel_type= 'CUSTOMER';
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.