Come posso fare un'istruzione UPDATE con JOIN in SQL Server?


1314

Ho bisogno di aggiornare questa tabella in SQL Server con i dati dalla sua tabella 'genitore', vedi sotto:

Tavolo: vendita

id (int)
udid (int)
assid (int)

Tabella: ud

id  (int)
assid  (int)

sale.assidcontiene il valore corretto da aggiornare ud.assid.

Quale query farà questo? Sto pensando a joinma non sono sicuro che sia possibile.


3
Quale RDBMS stai usando? MySQL, SQL Server, Oracle, PostgreSQL o qualcos'altro?
Chris J,

delle relazioni tra i tavoli? Come si può sapere quale record di vendita corrisponde a quale record di ud? Si basa sull'ID come chiave primaria in entrambe le tabelle?
Cătălin Pitiș,

Come puoi aggiornare UD? Ha solo l'assid e il proprio ID. Potresti dare un esempio in termini di valori reali esistenti e i record che desideri vengano modificati o aggiunti come risultato dello script?
Bernhard Hofmann,


Risposte:


2383

La sintassi dipende strettamente dal DBMS SQL in uso. Ecco alcuni modi per farlo in ANSI / ISO (aka dovrebbe funzionare su qualsiasi DBMS SQL), MySQL, SQL Server e Oracle. Tieni presente che il mio metodo ANSI / ISO suggerito sarà in genere molto più lento rispetto agli altri due metodi, ma se stai utilizzando un DBMS SQL diverso da MySQL, SQL Server o Oracle, potrebbe essere l'unica strada da percorrere (ad es. se il tuo DBMS SQL non supporta MERGE):

ANSI / ISO:

update ud 
     set assid = (
          select sale.assid 
          from sale 
          where sale.udid = ud.id
     )
 where exists (
      select * 
      from sale 
      where sale.udid = ud.id
 );

MySQL:

update ud u
inner join sale s on
    u.id = s.udid
set u.assid = s.assid

Server SQL:

update u
set u.assid = s.assid
from ud u
    inner join sale s on
        u.id = s.udid

PostgreSQL:

update ud
  set assid = s.assid
from sale s 
where ud.id = s.udid;

Si noti che la tabella di destinazione non deve essere ripetuta nella FROMclausola di Postgres.

Oracolo:

update
    (select
        u.assid as new_assid,
        s.assid as old_assid
    from ud u
        inner join sale s on
            u.id = s.udid) up
set up.new_assid = up.old_assid

SQLite:

update ud 
     set assid = (
          select sale.assid 
          from sale 
          where sale.udid = ud.id
     )
 where RowID in (
      select RowID 
      from ud 
      where sale.udid = ud.id
 );

3
Mi sembra che MySQL set assid = s.assiddovrebbe essere set u.assid = s.assid.
dotancohen,

2
Nella sintassi ANSI, cosa succede se SELECT dopo =restituisce più di una riga?
Getta via l'account

2
@ ThrowawayAccount3Million Probabilmente fallirebbe. AFAIK, questo tipo di operazione prevede un valore scalare e genera invece un errore se viene fornito un set di risultati.
Francis Lord,

6
Vorrei che l'OP scegliesse dei nomi migliori per la sua tabella e le sue colonne !! non è così leggibile / intuitivo ...
S.Serpooshan il

4
Postgre 9.3 funzionava solo usandoupdate ud set assid = s.assid
StackUnder

143

Questo dovrebbe funzionare in SQL Server:

update ud 
set assid = sale.assid
from sale
where sale.udid = id

98

Postgres

UPDATE table1
SET    COLUMN = value
FROM   table2,
       table3
WHERE  table1.column_id = table2.id
       AND table1.column_id = table3.id
       AND table1.COLUMN = value
       AND table2.COLUMN = value
       AND table3.COLUMN = value 

20
La risposta sarebbe più utile se utilizzasse i nomi di tabella / colonna utilizzati nella domanda. Perché ci sono 3 tabelle nella tua risposta?
Alfonso

50

Un approccio SQL standard sarebbe

UPDATE ud
SET assid = (SELECT assid FROM sale s WHERE ud.id=s.id)

Su SQL Server è possibile utilizzare un join

UPDATE ud
SET assid = s.assid
FROM ud u
JOIN sale s ON u.id=s.id

1
Con il primo, non puoi abbinare 2+ colonne, ma join funziona alla grande.
Makkok

6
@makciook: eh? Puoi semplicemente aggiungere più condizioni nella WHEREclausola se desideri abbinare colonne aggiuntive.
siride,

2
Solo un pò ... ma penso che l'OP significasse sale.udid = ud.id. E non sale.id.
Skippy VonDrake,

39

PostgreSQL :

CREATE TABLE ud (id integer, assid integer);
CREATE TABLE sales (id integer, udid integer, assid integer);

UPDATE ud
SET assid = sales.assid
FROM sales
WHERE sales.id = ud.id;

26

Query di aggiornamento semplificata utilizzando JOIN -ing più tabelle.

   UPDATE
        first_table ft
        JOIN second_table st ON st.some_id = ft.some_id
        JOIN third_table tt  ON tt.some_id = st.some_id
        .....
    SET
        ft.some_column = some_value
    WHERE ft.some_column = 123456 AND st.some_column = 123456

Nota : first_table, second_table, third_table e some_column come 123456 sono nomi di tabelle demo, nomi di colonne e ID. Sostituiscili con i nomi validi.


16

Un altro esempio del perché SQL non è realmente portatile.

Per MySQL sarebbe:

update ud, sale
set ud.assid = sale.assid
where sale.udid = ud.id;

Per maggiori informazioni leggi l'aggiornamento di più tabelle: http://dev.mysql.com/doc/refman/5.0/en/update.html

UPDATE [LOW_PRIORITY] [IGNORE] table_references
    SET col_name1={expr1|DEFAULT} [, col_name2={expr2|DEFAULT}] ...
    [WHERE where_condition]

2
+1 sul commento "perché SQL non è davvero portatile"! La portabilità è così fragile che solo dichiarando una variabile si romperà la portabilità tra molti dei motori di database popolari.
Jeff Moden,

8

Teradata Aster offre un altro modo interessante per raggiungere l'obiettivo:

MERGE INTO ud --what trable should be updated
USING sale -- from what table/relation update info should be taken
ON ud.id = sale.udid --join condition
WHEN MATCHED THEN 
    UPDATE SET ud.assid = sale.assid; -- how to update

8

Pensavo che quello di SQL Server nel primo post avrebbe funzionato per Sybase dato che sono entrambi T-SQL ma sfortunatamente no.

Per Sybase ho scoperto che l'aggiornamento deve essere sul tavolo stesso e non sull'alias:

update ud
set u.assid = s.assid
from ud u
    inner join sale s on
        u.id = s.udid

7

La seguente istruzione con la parola chiave FROM viene utilizzata per aggiornare più righe con un join

UPDATE users 
set users.DivisionId=divisions.DivisionId
from divisions join users on divisions.Name=users.Division

7

MySQL

Otterrai le prestazioni migliori se dimentichi la clausola where e inserisci tutte le condizioni nell'espressione ON.

Penso che ciò sia dovuto al fatto che la query deve prima aderire alle tabelle, quindi esegue la clausola where, quindi se è possibile ridurre ciò che è necessario per unirsi, questo è il modo più veloce per ottenere i risultati / eseguire udpate.

Esempio

Scenario

Hai una tabella di utenti. Possono accedere utilizzando il loro nome utente o e-mail o account_number. Questi account possono essere attivi (1) o inattivi (0). Questa tabella ha 50000 righe

Quindi hai una tabella di utenti da disabilitare in una sola volta perché scopri che hanno tutti fatto qualcosa di brutto. Questa tabella, tuttavia, ha una colonna con nomi utente, e-mail e numeri di account mescolati. Ha anche un indicatore "has_run" che deve essere impostato su 1 (true) quando è stato eseguito

domanda

UPDATE users User
    INNER JOIN
        blacklist_users BlacklistUser
        ON
        (
            User.username = BlacklistUser.account_ref
            OR
            User.email = BlacklistedUser.account_ref
            OR
            User.phone_number = BlacklistUser.account_ref
            AND
            User.is_active = 1
            AND
            BlacklistUser.has_run = 0
        )
    SET
        User.is_active = 0,
        BlacklistUser.has_run = 1;

Ragionamento

Se dovessimo unirci solo alle condizioni OR, essenzialmente dovrebbe controllare ogni riga 4 volte per vedere se dovrebbe unirsi e potenzialmente restituire molte più righe. Tuttavia, dandogli più condizioni, può "saltare" molte righe se non soddisfano tutte le condizioni quando si uniscono.

indennità

È più leggibile. Tutte le condizioni sono in un posto e le righe da aggiornare sono in un posto


4

E in MS ACCESS:

UPDATE ud 
INNER JOIN sale ON ud.id = sale.udid
SET ud.assid = sale.assid;

1
Come avvertenza, il SET deve venire immediatamente dopo la definizione del recordset! Ho appena provato a elaborare uno scenario simile in un database di Access, che aveva bisogno di una clausola WHERE (non lo avrebbe accettato come condizione ON valida). DOVE doveva venire per ultimo per evitare errori di sintassi.
Dodecaphone


3
UPDATE tblAppraisalBasicData
SET tblAppraisalBasicData.ISCbo=1
FROM tblAppraisalBasicData SI INNER JOIN  aaa_test RAN ON SI.EmpID = RAN.ID

3

Prova questo, penso che questo funzionerà per te

update ud

set ud.assid = sale.assid

from ud 

Inner join sale on ud.id = sale.udid

where sale.udid is not null

2

Per SQLite utilizzare la proprietà RowID per effettuare l'aggiornamento:

update Table set column = 'NewValue'
where RowID = 
(select t1.RowID from Table t1
join Table2 t2 on t1.JoinField = t2.JoinField
where t2.SelectValue = 'FooMyBarPlease');

1
Potresti spiegarlo un po '?
Mohammed Noureldin,

1
@MohammedNoureldin Proverò a spiegare. Il problema è come aggiornare una tabella con un risultato di una query su un join utilizzando la stessa tabella. L'istruzione (sub-select) si comporta come un join e restituisce un campo di sistema, RowID, che è un numero univoco per ogni riga in una tabella. Poiché la sottoselezione può restituire più righe, "where RowID =" seleziona una singola riga corretta dalla sottoselezione risultante ed esegue l'aggiornamento alla colonna. Fammi sapere se hai bisogno di ulteriori chiarimenti o hai bisogno di capire una variazione su questo tema.
KeithTheBiped,
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.