SQL aggiorna i campi di una tabella dai campi di un'altra


124

Ho due tavoli:

A [ID, column1, column2, column3]
B [ID, column1, column2, column3, column4]

Asarà sempre un sottoinsieme di B(il che significa che tutte le colonne di Asono anche in B).

Voglio aggiornare un record con uno specifico IDin Bcon i loro dati da Aper tutte le colonne di A. Questo IDesiste sia in Ache B.

Esiste una UPDATEsintassi o un altro modo per farlo senza specificare i nomi delle colonne, semplicemente dicendo "imposta tutte le colonne di A" ?

Sto usando PostgreSQL, quindi è accettato anche un comando specifico non standard (tuttavia, non preferito).


Penso che questo sia quello che vuoi fare, dba.stackexchange.com/a/58383
zubair-0

Risposte:


234

È possibile utilizzare la clausola FROM non standard .

UPDATE b
SET column1 = a.column1,
  column2 = a.column2,
  column3 = a.column3
FROM a
WHERE a.id = b.id
AND b.id = 1

9
La domanda sta chiedendo come farlo senza specificare tutti i nomi delle colonne. (E lo sono anch'io.)
cluesque

2
Sono d'accordo con @cluesque, ma questa risposta è un modo eccellente per utilizzare i valori in una colonna in una tabella come tabella di ricerca per sostituire i valori in una colonna in un'altra tabella (vedi SO 21657475 ), quindi +1 ...
Victoria Stuart

1
Perché b.id = 1 è necessario?
YasirAzgar

1
@YasirAzgar b.id = 1 serve a limitare le righe in b che vengono aggiornate. Altrimenti aggiorneremo ogni riga della tabella. Di tanto in tanto, potrebbe essere quello che vuoi. Ma la domanda originale era aggiornare una riga specifica in b.
Scott Bailey

Questo è ciò di cui avevo bisogno per il mio problema particolare: aggiornare la colonna di una tabella con i valori della colonna di un'altra tabella con un nome diverso.
muad-dweeb

49

La domanda è vecchia ma sentivo che la risposta migliore non era stata ancora data.

Esiste una UPDATEsintassi ... senza specificare i nomi delle colonne ?

Soluzione generale con SQL dinamico

Non è necessario conoscere alcun nome di colonna ad eccezione di alcune colonne univoche su cui unirsi ( idnell'esempio). Funziona in modo affidabile per ogni possibile caso d'angolo a cui riesco a pensare.

Questo è specifico di PostgreSQL. Sto costruendo codice dinamico basato su information_schema , in particolare sulla tabella information_schema.columns, che è definita nello standard SQL e la maggior parte dei principali RDBMS (eccetto Oracle) ce l'hanno. Ma DOun'istruzione con codice PL / pgSQL che esegue SQL dinamico è una sintassi PostgreSQL totalmente non standard.

DO
$do$
BEGIN

EXECUTE (
SELECT
  'UPDATE b
   SET   (' || string_agg(        quote_ident(column_name), ',') || ')
       = (' || string_agg('a.' || quote_ident(column_name), ',') || ')
   FROM   a
   WHERE  b.id = 123
   AND    a.id = b.id'
FROM   information_schema.columns
WHERE  table_name   = 'a'       -- table name, case sensitive
AND    table_schema = 'public'  -- schema name, case sensitive
AND    column_name <> 'id'      -- all columns except id
);

END
$do$;

Supponendo una colonna corrispondente in bper ogni colonna in a, ma non il contrario. bpuò avere colonne aggiuntive.

WHERE b.id = 123 è facoltativo, per aggiornare una riga selezionata.

SQL Fiddle.

Risposte correlate con ulteriori spiegazioni:

Soluzioni parziali con semplice SQL

Con elenco di colonne condivise

È ancora necessario conoscere l'elenco dei nomi di colonna condivisi da entrambe le tabelle. Con una scorciatoia sintattica per l'aggiornamento di più colonne, in ogni caso più breve di quanto suggerito finora dalle altre risposte.

UPDATE b
SET   (  column1,   column2,   column3)
    = (a.column1, a.column2, a.column3)
FROM   a
WHERE  b.id = 123    -- optional, to update only selected row
AND    a.id = b.id;

SQL Fiddle.

Questa sintassi è stata introdotta con Postgres 8.2 nel 2006, molto prima che venisse posta la domanda. Dettagli nel manuale.

Relazionato:

Con elenco di colonne in formato B

Se tutte le colonne di Asono definite NOT NULL(ma non necessariamente B),
e si conoscono i nomi delle colonne di B(ma non necessariamente A).

UPDATE b
SET   (column1, column2, column3, column4)
    = (COALESCE(ab.column1, b.column1)
     , COALESCE(ab.column2, b.column2)
     , COALESCE(ab.column3, b.column3)
     , COALESCE(ab.column4, b.column4)
      )
FROM (
   SELECT *
   FROM   a
   NATURAL LEFT JOIN  b -- append missing columns
   WHERE  b.id IS NULL  -- only if anything actually changes
   AND    a.id = 123    -- optional, to update only selected row
   ) ab
WHERE b.id = ab.id;

Il NATURAL LEFT JOINunisce una riga da bcui tutte le colonne con lo stesso nome contengono gli stessi valori. Non abbiamo bisogno di un aggiornamento in questo caso (non cambia nulla) e possiamo eliminare quelle righe all'inizio del processo ( WHERE b.id IS NULL).
Abbiamo ancora bisogno di trovare una riga corrispondente, quindi b.id = ab.idnella query esterna.

db <> fiddle qui
Vecchio sqlfiddle.

Questo è l'SQL standard ad eccezione della FROMclausola .
Funziona indipendentemente da quale delle colonne sia effettivamente presente A, ma la query non è in grado di distinguere tra i valori NULL effettivi e le colonne mancanti in A, quindi è affidabile solo se tutte le colonne in Asono definite NOT NULL.

Ci sono più varianti possibili, a seconda di ciò che sai su entrambe le tabelle.


La potenza di SQL! Ho appena notato quando aggiungi parentesi nella clausola set ( SET (column1) = (a.column)) Postgres lo tratterà come un altro tipo di aggiornamento e darà un errore come questo:source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression
Edgar Ortega,

26

Ho lavorato con il database IBM DB2 per più di dieci anni e ora sto cercando di imparare PostgreSQL.

Funziona su PostgreSQL 9.3.4, ma non funziona su DB2 10.5:

UPDATE B SET
     COLUMN1 = A.COLUMN1,
     COLUMN2 = A.COLUMN2,
     COLUMN3 = A.COLUMN3
FROM A
WHERE A.ID = B.ID

Nota: il problema principale è la causa FROM che non è supportata in DB2 e non in ANSI SQL.

Funziona su DB2 10.5, ma NON su PostgreSQL 9.3.4:

UPDATE B SET
    (COLUMN1, COLUMN2, COLUMN3) =
               (SELECT COLUMN1, COLUMN2, COLUMN3 FROM A WHERE ID = B.ID)

FINALMENTE! Funziona sia su PostgreSQL 9.3.4 che su DB2 10.5:

UPDATE B SET
     COLUMN1 = (SELECT COLUMN1 FROM A WHERE ID = B.ID),
     COLUMN2 = (SELECT COLUMN2 FROM A WHERE ID = B.ID),
     COLUMN3 = (SELECT COLUMN3 FROM A WHERE ID = B.ID)

3
Notare che la seconda e la terza query non sono completamente equivalenti alla prima. Se non viene trovata alcuna riga corrispondente B, la prima istruzione non fa nulla (la riga originale rimane invariata), mentre le altre due sovrascrivono le colonne con valori NULL.
Erwin Brandstetter

7

Questo è di grande aiuto. Il codice

UPDATE tbl_b b
SET   (  column1,   column2,   column3)
    = (a.column1, a.column2, a.column3)
FROM   tbl_a a
WHERE  b.id = 1
AND    a.id = b.id;

funziona perfettamente.

notato che hai bisogno di una parentesi "" in

From "tbl_a" a

per farlo funzionare.


5

Non necessariamente quello che hai chiesto, ma forse l'uso dell'eredità postgres potrebbe aiutare?

CREATE TABLE A (
    ID            int,
    column1       text,
    column2       text,
    column3       text
);

CREATE TABLE B (
    column4       text
) INHERITS (A);

Ciò evita la necessità di aggiornare B.

Ma assicurati di leggere tutti i dettagli .

Altrimenti, ciò che chiedi non è considerato una buona pratica: le cose dinamiche come le visualizzazioni con SELECT * ...sono scoraggiate (poiché una tale leggera comodità potrebbe rompere più cose che aiutare le cose), e ciò che chiedi sarebbe equivalente al UPDATE ... SETcomando.


Non sono sicuro di come l'eredità risolverà questo problema. Intendi aggiungere un trigger di aggiornamento per A che aggiorna anche B? Non voglio sincronizzare A con B tutto il tempo, solo su richiesta. E in tal caso, non posso usare i trigger.
Nir

2
Sì, se è solo in alcuni casi, l'ereditarietà non funzionerebbe e in tal caso sconsiglio l'approccio di query dinamico. (ci sono ancora modi per ottenere questo risultato utilizzando linguaggi procedurali postgres. inoltre, se si desidera utilizzare i trigger, è possibile utilizzarli anche - aggiungendo il campo di sincronizzazione, ad esempio il trigger di attivazione solo quando è impostato).
Irragionevole

0

puoi costruire ed eseguire sql dinamico per farlo, ma non è davvero l'ideale


Ci ho pensato. Pensavo di poter rendere la mia query compatibile con le modifiche successive a entrambe le tabelle, ma sql dinamico sembra essere troppo complicato rispetto a specificare tutti i campi e dimenticare la compatibilità con le versioni successive.
Nir

sì, sarà complicato, ma dovrebbe essere compatibile con le successive colonne aggiunte o rimosse. Dovrai prima eseguire una query per ottenere i nomi delle colonne da entrambe le tabelle, quindi abbinare i nomi delle colonne e quindi scrivere lo sql dinamico per eseguire l'aggiornamento in base ai nomi delle colonne corrispondenti. un progetto divertente in realtà :)
Daniel Brink

-4

Prova a seguire

Update A a, B b, SET a.column1=b.column1 where b.id=1

MODIFICATO: - Aggiorna più di una colonna

Update A a, B b, SET a.column1=b.column1, a.column2=b.column2 where b.id=1

Non capisco come copia column1, column2 e column3. E ho bisogno di menzionare esplicitamente la colonna 1.
Nir

Non funziona per me. Ottengo il seguente errore: ERRORE: errore di sintassi in corrispondenza o vicino a ","
melbic

1
Questa sintassi non standard funzionerebbe UPDATEin MySQL , ma non è valida per PostgreSQL.
Erwin Brandstetter
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.