Campos de atualização SQL de uma tabela dos campos de outra

124

Eu tenho duas mesas:

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

Asempre será um subconjunto de B(ou seja, todas as colunas de Atambém estão dentro B).

Quero atualizar um registro com um específico IDno Bcom os seus dados a partir Ade todas as colunas de A. Isso IDexiste em Ae B.

Existe uma UPDATEsintaxe ou qualquer outra maneira de fazer isso sem especificar os nomes das colunas, apenas dizendo "definir todas as colunas de A" ?

Como estou usando o PostgreSQL, um comando não padrão específico também é aceito (no entanto, não é preferido).

Nir
fonte
Eu acho que isso é o que você quer fazer, dba.stackexchange.com/a/58383
zubair-0

Respostas:

234

Você pode usar a cláusula FROM não padrão .

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
fonte
9
A pergunta é sobre como fazê-lo sem especificar todos os nomes de colunas. (E eu também sou.)
cluesque
2
Concordo com @cluesque, mas esta resposta é uma excelente maneira de usar valores em uma coluna de uma tabela como tabela de pesquisa para substituir valores em uma coluna de outra tabela (consulte o SO 21657475 ), então +1 ...
Victoria Stuart
1
Por que b.id = 1 é necessário?
YasirAzgar #
1
@YasirAzgar o b.id = 1 é para limitar quais linhas em b são atualizadas. Caso contrário, atualizaríamos todas as linhas da tabela. Ocasionalmente, isso pode ser o que você deseja. Mas a pergunta original era atualizar uma linha específica em b.
21918 Scott
Isso é o que eu precisava para o meu problema específico: atualizar a coluna de uma tabela com valores da coluna de nome diferente de outra tabela.
muad-dweeb 31/03
49

A pergunta é antiga, mas eu senti que a melhor resposta ainda não havia sido dada.

Existe uma UPDATEsintaxe ... sem especificar os nomes das colunas ?

Solução geral com SQL dinâmico

Você não precisa saber nenhum nome de coluna, exceto algumas colunas exclusivas para ingressar ( idno exemplo). Funciona de forma confiável para qualquer possível gabinete de canto em que eu possa pensar.

Isso é específico do PostgreSQL. Estou construindo código dinâmico com base no information_schema , em particular na tabela information_schema.columns, definida no padrão SQL e na maioria dos principais RDBMS (exceto Oracle). Mas uma DOdeclaração com o código PL / pgSQL executando SQL dinâmico é uma sintaxe PostgreSQL totalmente fora do padrão.

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

Assumindo uma coluna correspondente bpara cada coluna a, mas não o contrário. bpode ter colunas adicionais.

WHERE b.id = 123 é opcional, para atualizar uma linha selecionada.

SQL Fiddle.

Respostas relacionadas com mais explicações:

Soluções parciais com SQL simples

Com lista de colunas compartilhadas

Você ainda precisa conhecer a lista de nomes de colunas que as duas tabelas compartilham. Com um atalho de sintaxe para atualizar várias colunas - menor que o que as outras respostas sugeriram até agora em qualquer caso.

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.

Essa sintaxe foi introduzida no Postgres 8.2 em 2006, muito antes da pergunta ser feita. Detalhes no manual.

Palavras-chave:

Com lista de colunas em B

Se todas as colunas de Aestiverem definidas NOT NULL(mas não necessariamente B)
e você souber os nomes das colunas de B(mas não 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;

A NATURAL LEFT JOINjunta-se uma linha de bonde todas as colunas com o mesmo nome segurar mesmos valores. Não precisamos de atualização neste caso (nada muda) e podemos eliminar essas linhas no início do processo ( WHERE b.id IS NULL).
Ainda precisamos encontrar uma linha correspondente, portanto, b.id = ab.idna consulta externa.

db <> mexe aqui
Old sqlfiddle.

Este é o SQL padrão, exceto a FROMcláusula .
Funciona independentemente de qual das colunas estiver realmente presente A, mas a consulta não pode distinguir entre valores NULL reais e colunas ausentes A, portanto, só é confiável se todas as colunas Aestiverem definidas NOT NULL.

Existem várias variações possíveis, dependendo do que você sabe sobre as duas tabelas.

Erwin Brandstetter
fonte
O poder do SQL! Só notei quando você adicionar parêntese na cláusula set ( SET (column1) = (a.column)) Postgres irá tratá-lo como um outro tipo de atualização e dar e erro como este:source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression
Edgar Ortega
26

Trabalho com o banco de dados IBM DB2 há mais de uma década e agora estou tentando aprender o PostgreSQL.

Ele funciona no PostgreSQL 9.3.4, mas não no DB2 10.5:

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

Nota: O principal problema é da causa FROM que não é suportada no DB2 e também não no ANSI SQL.

Ele funciona no DB2 10.5, mas NÃO funciona no PostgreSQL 9.3.4:

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

FINALMENTE! Ele funciona no PostgreSQL 9.3.4 e no 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
fonte
3
Observe que a segunda e a terceira consultas não são completamente equivalentes à primeira. Se nenhuma linha correspondente for encontrada B, a primeira instrução não fará nada (a linha original permanece intocada), enquanto as outras duas sobrescrevem colunas com valores NULL.
Erwin Brandstetter
7

Esta é uma grande ajuda. O código

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;

funciona perfeitamente.

observou que você precisa de um suporte "" no

From "tbl_a" a

para fazer funcionar.

user2493970
fonte
5

Não necessariamente o que você pediu, mas talvez o uso da herança do postgres possa ajudar?

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

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

Isso evita a necessidade de atualização B.

Mas não deixe de ler todos os detalhes .

Caso contrário, o que você solicita não é considerado uma boa prática - coisas dinâmicas, como visualizações com, SELECT * ...são desencorajadas (como essa pequena conveniência pode quebrar mais coisas do que ajudar), e o que você solicita seria equivalente ao UPDATE ... SETcomando.

Desrazão
fonte
Não tenho certeza de como a herança resolverá isso. Você quer dizer adicionar um gatilho de atualização para A que também atualiza B? Não quero sincronizar A com B o tempo todo, apenas mediante solicitação. E, nesse caso, não posso usar os gatilhos.
Nir
2
Sim, se for apenas em certos casos, a herança não funcionaria e, nesse caso, desaconselho a abordagem de consulta dinâmica. (ainda existem maneiras de conseguir isso usando linguagens processuais do postgres. também se você deseja usar gatilhos, também pode usá-los - adicionando campo de sincronização, por exemplo, disparando gatilho somente quando está definido).
Unreason
0

você pode criar e executar sql dinâmico para fazer isso, mas não é realmente ideal

Daniel Brink
fonte
Eu pensei sobre isso. Eu pensei que poderia tornar minha consulta compatível com alterações posteriores nas duas tabelas, mas o sql dinâmico parece ser muito complicado do que apenas especificar todos os campos e esquecer a compatibilidade direta.
Nir
Sim, será complicado, mas deve ser compatível com versões posteriores que forem adicionadas ou removidas. Você precisará primeiro fazer uma consulta para obter os nomes das colunas de ambas as tabelas, depois corresponder aos nomes das colunas e depois escrever o sql dinâmico para fazer a atualização com base nos nomes das colunas correspondentes. um projeto divertido, na verdade :)
Daniel Brink
-4

Tente seguir

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

EDITADO: - Atualize mais de uma coluna

Update A a, B b, SET a.column1=b.column1, a.column2=b.column2 where b.id=1
Salil
fonte
Não entendo como ele copia a coluna1, a coluna2 e a coluna3. E eu preciso mencionar explicitamente a coluna1.
Nir
Não funciona para mim. Eu recebo o seguinte erro: ERRO: erro de sintaxe em ou próximo a ","
melbic 25/06
1
Essa sintaxe não padrão funcionaria UPDATEno MySQL , mas é inválida para o PostgreSQL.
Erwin Brandstetter