Pola aktualizacji SQL jednej tabeli z pól innej tabeli

124

Mam dwa stoliki:

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

Azawsze będzie podzbiorem B(co oznacza, że ​​wszystkie kolumny Asą również w B).

Chcę zaktualizować rekord o konkretny IDw Bz ich danymi z Adla wszystkich kolumn A. To IDistnieje zarówno w, jak Ai B.

Czy istnieje UPDATEskładnia lub inny sposób na zrobienie tego bez podawania nazw kolumn, po prostu mówiąc „ustaw wszystkie kolumny A” ?

Używam PostgreSQL, więc akceptowana jest również określona niestandardowa komenda (jednak nie jest preferowana).

Nir
źródło
Myślę, że to jest to, co chcesz zrobić, dba.stackexchange.com/a/58383
zubair-0

Odpowiedzi:

235

Możesz użyć niestandardowej klauzuli FROM .

UPDATE b
SET column1 = a.column1,
  column2 = a.column2,
  column3 = a.column3
FROM a
WHERE a.id = b.id
AND b.id = 1
Scott Bailey
źródło
9
Pytanie brzmi, jak to zrobić bez podawania wszystkich nazw kolumn. (I ja też.)
wskazówki
2
Zgadzam się z @cluesque, ale ta odpowiedź jest doskonałym sposobem na użycie wartości w jednej kolumnie tabeli jako tabeli odnośników do zastąpienia wartości w kolumnie w innej tabeli (patrz SO 21657475 ), więc +1 ...
Victoria Stuart
1
Dlaczego b.id = 1 jest potrzebne?
YasirAzgar
1
@YasirAzgar the b.id = 1 ma na celu ograniczenie aktualizowanych wierszy w b. W przeciwnym razie zaktualizowalibyśmy każdy wiersz w tabeli. Czasami może to być to, czego chcesz. Ale pierwotnym pytaniem było zaktualizowanie określonego wiersza w b.
Scott Bailey
Właśnie tego potrzebowałem w przypadku mojego konkretnego problemu: zaktualizowanie kolumny jednej tabeli wartościami z kolumny innej tabeli o innej nazwie.
muad-dweeb
49

Pytanie jest stare, ale czułem, że nie otrzymałem jeszcze najlepszej odpowiedzi.

Czy istnieje UPDATEskładnia ... bez określania nazw kolumn ?

Ogólne rozwiązanie z dynamicznym SQL

Nie musisz znać żadnych nazw kolumn poza kilkoma unikatowymi kolumnami, do których chcesz dołączyć ( idw przykładzie). Działa niezawodnie w każdym możliwym przypadku narożnym, o którym mogę pomyśleć.

Jest to specyficzne dla PostgreSQL. Buduję kod dynamiczny w oparciu o schemat information_schema , w szczególności tabelę information_schema.columns, która jest zdefiniowana w standardzie SQL i ma ją większość głównych RDBMS (poza Oracle). Ale DOinstrukcja z kodem PL / pgSQL wykonującym dynamiczny SQL jest całkowicie niestandardową składnią PostgreSQL.

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$;

Zakładając pasującą kolumnę w bdla każdej kolumny w a, ale nie odwrotnie. bmoże mieć dodatkowe kolumny.

WHERE b.id = 123 jest opcjonalne, aby zaktualizować wybrany wiersz.

SQL Fiddle.

Powiązane odpowiedzi z dokładniejszym wyjaśnieniem:

Częściowe rozwiązania ze zwykłym SQL

Z listą wspólnych kolumn

Nadal musisz znać listę nazw kolumn, które współużytkują obie tabele. Ze skrótem do składni do aktualizowania wielu kolumn - w każdym przypadku krótszym niż sugerowały inne odpowiedzi.

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.

Ta składnia została wprowadzona w Postgres 8.2 w 2006 roku, na długo przed zadaniem pytania. Szczegóły w instrukcji.

Związane z:

Z listą kolumn w formacie B

Jeśli wszystkie kolumny Asą zdefiniowane NOT NULL(ale nie koniecznie B),
a ty znać nazwy kolumn z B(ale nie koniecznie 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;

NATURAL LEFT JOINDołącza do wiersza z bktórym wszystkie kolumny o tej samej nazwie posiadają te same wartości. W tym przypadku nie potrzebujemy aktualizacji (nic się nie zmienia) i możemy wyeliminować te wiersze na wczesnym etapie procesu ( WHERE b.id IS NULL).
Nadal musimy znaleźć pasujący wiersz, więc b.id = ab.idw zewnętrznym zapytaniu.

db <> skrzypce tutaj
Stare sqlfiddle.

To jest standardowy SQL z wyjątkiem FROMklauzuli .
Działa bez względu na to, która z kolumn jest faktycznie obecna w programie A, ale zapytanie nie może odróżnić rzeczywistych wartości NULL od brakujących kolumn w A, więc jest wiarygodne tylko wtedy, gdy Azdefiniowane są wszystkie kolumny w NOT NULL.

Istnieje wiele możliwych odmian, w zależności od tego, co wiesz o obu stołach.

Erwin Brandstetter
źródło
Potęga SQL! Właśnie zauważyłem, że dodasz nawiasy w klauzuli set ( SET (column1) = (a.column)) Postgres potraktuje to jako inny rodzaj aktualizacji i poda i błąd w ten sposób:source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression
Edgar Ortega
26

Pracuję z bazą danych IBM DB2 od ponad dziesięciu lat, a teraz próbuję nauczyć się PostgreSQL.

Działa na PostgreSQL 9.3.4, ale nie działa na DB2 10.5:

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

Uwaga: Główny problem dotyczy przyczyny OD, która nie jest obsługiwana w programie DB2, a także w języku ANSI SQL.

Działa na DB2 10.5, ale NIE działa na PostgreSQL 9.3.4:

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

WRESZCIE! Działa na PostgreSQL 9.3.4 i 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)
jochan
źródło
3
Zwróć uwagę, że drugie i trzecie zapytanie nie są w pełni równoważne z pierwszym. Jeśli nie zostanie znaleziony żaden pasujący wiersz B, pierwsza instrukcja nic nie robi (oryginalny wiersz pozostaje nietknięty), podczas gdy pozostałe dwie nadpisują kolumny wartościami NULL.
Erwin Brandstetter
7

To jest wielka pomoc. Kod

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;

działa świetnie.

zauważył, że potrzebujesz nawiasu „” w

From "tbl_a" a

żeby to działało.

user2493970
źródło
5

Niekoniecznie to, o co pytałeś, ale może użycie dziedziczenia postgres może pomóc?

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

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

Pozwala to uniknąć konieczności aktualizacji B.

Ale pamiętaj, aby przeczytać wszystkie szczegóły .

W przeciwnym razie to, o co prosisz, nie jest uważane za dobrą praktykę - dynamiczne rzeczy, takie jak widoki z, SELECT * ...są odradzane (ponieważ taka niewielka wygoda może zepsuć więcej rzeczy niż pomoc), a to, o co prosisz, byłoby równoważne dla UPDATE ... SETpolecenia.

Bez powodu
źródło
Nie jestem pewien, jak dziedziczenie to rozwiąże. Czy masz na myśli dodanie wyzwalacza aktualizacji dla A, który również aktualizuje B? Nie chcę synchronizować A z B przez cały czas, tylko na żądanie. I w takim przypadku nie mogę użyć wyzwalaczy.
Nir
2
Tak, gdyby tak było tylko w określonych przypadkach, to dziedziczenie nie działałoby iw takim przypadku odradzam dynamiczne podejście do zapytań. (nadal istnieją sposoby na osiągnięcie tego za pomocą języków proceduralnych postgres. również jeśli chcesz używać wyzwalaczy, możesz ich również użyć - dodając pole synchronizacji, na przykład wyzwalanie wyzwalacza tylko wtedy, gdy jest ustawione).
Bez powodu
0

możesz w tym celu zbudować i uruchomić dynamiczny sql, ale nie jest to idealne rozwiązanie

Daniel Brink
źródło
Myślałem o tym. Pomyślałem, że mógłbym dostosować moje zapytanie do późniejszych zmian w obu tabelach, ale dynamiczny sql wydaje się być zbyt skomplikowany niż po prostu określić wszystkie pola i zapomnieć o kompatybilności w przód.
Nir
tak, będzie to skomplikowane, ale powinno być kompatybilne z późniejszymi dodawanymi lub usuwanymi kolumnami. Najpierw musisz wykonać zapytanie, aby uzyskać nazwy kolumn z obu tabel, a następnie dopasować nazwy kolumn, a następnie napisać dynamiczny plik sql, aby wykonać aktualizację na podstawie pasujących nazw kolumn. naprawdę fajny projekt :)
Daniel Brink
-4

Spróbuj obserwować

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

ZMIENIONO: - Zaktualizuj więcej niż jedną kolumnę

Update A a, B b, SET a.column1=b.column1, a.column2=b.column2 where b.id=1
Salil
źródło
Nie rozumiem, jak kopiuje column1, column2 i column3. I muszę wyraźnie wspomnieć o kolumnie column1.
Nir
Nie działa na mnie.
Pojawia
1
Ta niestandardowa składnia działałaby UPDATEw MySQL , ale jest nieprawidłowa dla PostgreSQL.
Erwin Brandstetter