Oracle SQL: Zaktualizuj tabelę o dane z innej tabeli

251

Tabela 1:

id    name    desc
-----------------------
1     a       abc
2     b       def
3     c       adf

Tabela 2:

id    name    desc
-----------------------
1     x       123
2     y       345

W Oracle SQL, jak uruchomić kwerendę aktualizacyjną SQL, która może aktualizować tabelę 1 za pomocą tabeli 2 namei descużywać tego samego id? Więc końcowy wynik, który otrzymam, to

Tabela 1:

id    name    desc
-----------------------
1     x       123
2     y       345
3     c       adf

Pytanie pochodzi z aktualizacji jednej tabeli danymi z innej , ale specjalnie dla Oracle SQL.

Muhd
źródło
Musisz wrócić do drugiego pytania, odrzucić tę odpowiedź i stwierdzić, że potrzebujesz składni Oracle PLSQL.
p.campbell
3
@ p.campbell, To nie jest moje pytanie ...
Muhd,
1
Rozumiem. Więc skopiowałeś treść pytania, ale zmodyfikowałeś ją, by zawierała bit Oracle.
p.campbell
2
Tak. I to chyba nie jest najlepszy przykład, ponieważ „desc” to słowo zastrzeżone, ale no cóż.
Muhd,

Odpowiedzi:

512

Nazywa się to aktualizacją skorelowaną

UPDATE table1 t1
   SET (name, desc) = (SELECT t2.name, t2.desc
                         FROM table2 t2
                        WHERE t1.id = t2.id)
 WHERE EXISTS (
    SELECT 1
      FROM table2 t2
     WHERE t1.id = t2.id )

Zakładając, że wyniki łączenia zostaną wyświetlone w zachowanym kluczu, możesz również

UPDATE (SELECT t1.id, 
               t1.name name1,
               t1.desc desc1,
               t2.name name2,
               t2.desc desc2
          FROM table1 t1,
               table2 t2
         WHERE t1.id = t2.id)
   SET name1 = name2,
       desc1 = desc2
Justin Cave
źródło
8
W pierwszym przykładzie: Czy zewnętrzna klauzula WHERE jest niezbędna do poprawnych wyników? A może używasz go tylko do przyspieszenia zapytania?
Mathias Bader,
41
@totoro - W pierwszym przykładzie WHERE EXISTSuniemożliwia aktualizację wiersza w, t1jeśli nie ma pasującego wiersza w t2. Bez tego każdy wiersz w t1zostanie zaktualizowany, a wartości zostaną ustawione na, NULLjeśli nie ma pasującego wiersza w t2. Zasadniczo nie jest tak, jak chcesz, więc WHERE EXISTSjest to na ogół potrzebne.
Justin Cave
3
Warto dodać, że SELECT ... FROM t2 wynikiem musi być unikalny wiersz. Oznacza to, że musisz wybrać wszystkie pola, które zawierają unikalny klucz - nieunikalny klucz podstawowy nie jest wystarczający. Bez wyjątkowości zostajesz zredukowany do czegoś w rodzaju pętli @ PaulKarr - a jeśli nie ma unikalnej korelacji, dla każdego wiersza źródłowego można zaktualizować więcej niż jeden wiersz docelowy.
Andrew Leach,
2
Objaśnienie zachowanego klucza wymaganego dla złączeń aktualizowanych: asktom.oracle.com/pls/asktom/…
Vadzim
1
@RachitSharma - Oznacza to, że twoje podzapytanie (zapytanie od table2) zwraca wiele wierszy dla jednej lub więcej table1wartości, a Oracle nie wie, którego chcesz użyć. Zwykle oznacza to, że musisz udoskonalić podkwerendę, aby zwróciła pojedynczy odrębny wiersz.
Justin Cave
132

Spróbuj tego:

MERGE INTO table1 t1
USING
(
-- For more complicated queries you can use WITH clause here
SELECT * FROM table2
)t2
ON(t1.id = t2.id)
WHEN MATCHED THEN UPDATE SET
t1.name = t2.name,
t1.desc = t2.desc;
Adrian
źródło
4
Rzeczywiście bardzo szybko, 1159477 rzędów połączyło się w 15,5s
jefissu
3
Mam nadzieję, że wszyscy odwiedzający to pytanie po 2015 roku zauważą tę odpowiedź. Zauważ, że to działa również, jeśli table1i table2są tą samą tabelą, po prostu ONuważaj na -part i WHERE-lause dla -statement SELECTof table2!
sjngm
1
Uważam, że za każdym razem, gdy muszę wykonać kolejną fuzję, wracam do tej odpowiedzi w poszukiwaniu inspiracji. Mogę go wydrukować i oprawić na ścianie
co roku
Działa jak urok !! Dzięki!
davidwillianx
WYBIERZ IDENTYFIKATOR ODNIESIENIA, FIELD1, FIELD1 Z tabeli2 GDZIE ID NIE JEST NULL
Joseph Poirier
17

próbować

UPDATE Table1 T1 SET
T1.name = (SELECT T2.name FROM Table2 T2 WHERE T2.id = T1.id),
T1.desc = (SELECT T2.desc FROM Table2 T2 WHERE T2.id = T1.id)
WHERE T1.id IN (SELECT T2.id FROM Table2 T2 WHERE T2.id = T1.id);
Yahia
źródło
4
Minusem tego jest to, że instrukcja SELECT jest powtarzana 3 razy. W złożonych przykładach, które mogą stanowić przełom.
David Balažic
9
Update table set column = (select...)

nigdy dla mnie nie działało, ponieważ zestaw oczekuje tylko 1 wartości - Błąd SQL: ORA-01427: jedno-wierszowe podzapytanie zwraca więcej niż jeden wiersz.

oto rozwiązanie:

BEGIN
For i in (select id, name, desc from table1) 
LOOP
Update table2 set name = i.name, desc = i.desc where id = i.id;
END LOOP;
END;

Tak właśnie uruchamiasz go w arkuszu SQLDeveloper. Mówią, że jest powolny, ale to jedyne rozwiązanie, które działało dla mnie w tej sprawie.

Pau Karr
źródło
czy ktoś może wyjaśnić, dlaczego zasługuje na -2 reputację? LOL.
Pau Karr
13
Nie obniżyłem stawki, ale to nie jest dobre rozwiązanie. Po pierwsze: jeśli podselekcja zwracała wiele wartości, wówczas pętla for będzie nadpisywać nazwę na table2 wiele razy dla niektórych / wszystkich rekordów (nieczyszczonych). Po drugie: nie ma kolejności według klauzuli, więc nastąpi to w nieprzewidywalny sposób (tzn. Wygrywa ostatnia wartość w nieuporządkowanych danych). Po trzecie: będzie znacznie wolniej. Zakładając, że wynik był przeznaczony dla pętli, oryginalny SUBSELECT mógł zostać przepisany w jakiś sposób kontrolowany, aby powrócić tylko 1 wartość dla każdego rekordu ... Najprostszym sposobem byłoby wymyślony (wybierz min (nazwa) ...)
Alternator
Właśnie tego potrzebowałem. Dzięki (+1)
Robert Hyatt
3
Jeśli otrzymujesz wiele wartości w swoim podzapytaniu, możesz ponownie przemyśleć zapytanie i użyć DISTINCT lub GROUP BY z MIN, MAX. Po prostu pomysł.
Francis,
Krótko mówiąc: jeśli w ogóle możesz tego uniknąć, nigdy NIGDY nie używaj PĘTLI w instrukcji T-SQL. Osobiście, gdyby nie było 0,001% czasu, kiedy nie ma innego rozwiązania, nawet nie sądzę, że powinna to być nawet dostępna funkcja w T-SQL. T-SQL został zaprojektowany w taki sposób, aby był oparty na zestawach, więc działa na całych zestawach danych jako całości; NIE należy go używać do pracy z danymi linia po linii.
Ray K.
8

Wydaje się, że jest to jeszcze lepsza odpowiedź z klauzulą ​​„in”, która pozwala na wiele kluczy dla złączenia :

update fp_active set STATE='E', 
   LAST_DATE_MAJ = sysdate where (client,code) in (select (client,code) from fp_detail
  where valid = 1) ...

Wołowina polega na tym, że kolumny mają być używane jako klucz w nawiasach w klauzuli where przed „in” i mieć instrukcję select o takich samych nazwach kolumn w nawiasach. gdzie ( kolumna 1, kolumna 2 ) w ( wybierz ( kolumna 1, kolumna 2 ) z tabeli, w której „zestaw chcę” ;

Mrówka
źródło
Link wygasł. ( 404)
Dumbo
-3

Jeśli twoja tabela t1 i jej kopia zapasowa t2 mają wiele kolumn, oto kompaktowy sposób na zrobienie tego.

Ponadto moim powiązanym problemem było to, że tylko niektóre kolumny zostały zmodyfikowane, a wiele wierszy nie było edytowanych w tych kolumnach, więc chciałem zostawić je w spokoju - w zasadzie przywróć podzbiór kolumn z kopii zapasowej całej tabeli. Jeśli chcesz przywrócić wszystkie wiersze, pomiń klauzulę where.

Oczywiście najprościej byłoby usunąć i wstawić jako zaznaczone, ale w moim przypadku potrzebowałem rozwiązania z tylko aktualizacjami.

Sztuczka polega na tym, że gdy wybierzesz * z pary tabel ze zduplikowanymi nazwami kolumn, druga z nich otrzyma nazwę _1. Oto co wymyśliłem:

  update (
    select * from t1 join t2 on t2.id = t1.id
    where id in (
      select id from (
        select id, col1, col2, ... from t2
        minus select id, col1, col2, ... from t1
      )
    )
  ) set col1=col1_1, col2=col2_1, ...
Jim P.
źródło
To nie działa dla mnie w Oracle 11g. Czy możesz stworzyć działający przykład tej metody?
Jon Heller
-3
BEGIN
For i in (select id, name, desc from table2) 
LOOP
Update table1 set name = i.name, desc = i.desc where id = i.id and (name is null or desc is null);
END LOOP;
END;
Avila Theresa
źródło