Campos de actualización SQL de una tabla desde campos de otra

124

Tengo dos mesas:

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

Asiempre estará subconjunto de B(lo que significa que todas las columnas de Atambién están en B).

Quiero actualizar un registro con una específica IDen Bcon sus datos de Apara todas las columnas de A. Esta IDexiste tanto en Ay B.

¿Existe una UPDATEsintaxis o alguna otra forma de hacerlo sin especificar los nombres de las columnas, simplemente diciendo "establecer todas las columnas de A" ?

Estoy usando PostgreSQL, por lo que también se acepta un comando no estándar específico (sin embargo, no es preferido).

Nir
fuente
Creo que esto es lo que quieres hacer, dba.stackexchange.com/a/58383
zubair-0

Respuestas:

234

Puede usar la cláusula FROM no estándar .

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
fuente
9
La pregunta es cómo hacerlo sin especificar todos los nombres de columna. (Y yo también)
Cluesque
2
Estoy de acuerdo con @cluesque, pero esta respuesta es una excelente manera de usar valores en una columna de una tabla como una tabla de búsqueda para reemplazar valores en una columna en otra tabla (ver SO 21657475 ), entonces +1 ...
Victoria Stuart
1
¿Por qué se necesita b.id = 1?
YasirAzgar
1
@YasirAzgar the b.id = 1 es limitar qué filas en b se actualizan. De lo contrario, actualizaríamos cada fila de la tabla. Ocasionalmente, eso podría ser lo que quieres. Pero la pregunta original era actualizar una fila específica en b.
Scott Bailey
Esto es lo que necesitaba para mi problema particular: actualizar la columna de una tabla con valores de la columna con el nombre diferente de otra tabla.
muad-dweeb
49

La pregunta es antigua pero sentí que la mejor respuesta aún no se había dado.

¿Hay una UPDATEsintaxis ... sin especificar los nombres de columna ?

Solución general con SQL dinámico

No necesita saber ningún nombre de columna, excepto algunas columnas únicas para unirse ( iden el ejemplo). Funciona de manera confiable para cualquier posible caso de esquina que se me ocurra.

Esto es específico de PostgreSQL. Estoy construyendo código dinámico basado en el esquema de información , en particular la tabla information_schema.columns, que se define en el estándar SQL y la mayoría de los RDBMS principales (excepto Oracle) lo tienen. Pero una DOdeclaración con código PL / pgSQL que ejecuta SQL dinámico es una sintaxis PostgreSQL totalmente no estándar.

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

Asumiendo una columna coincidente bpara cada columna a, pero no al revés. bpuede tener columnas adicionales.

WHERE b.id = 123 es opcional, para actualizar una fila seleccionada.

SQL Fiddle.

Respuestas relacionadas con más explicaciones:

Soluciones parciales con SQL simple

Con lista de columnas compartidas

Aún necesita conocer la lista de nombres de columna que comparten ambas tablas. Con un acceso directo de sintaxis para actualizar varias columnas, más corto que lo que otras respuestas sugirieron hasta ahora en cualquier 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.

Esta sintaxis se introdujo con Postgres 8.2 en 2006, mucho antes de que se hiciera la pregunta. Detalles en el manual.

Relacionado:

Con lista de columnas en B

Si todas las columnas de Aestán definidos NOT NULL(pero no necesariamente B),
y se conocen los nombres de las columnas de B(pero no necesariamente 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;

Se NATURAL LEFT JOINune a una fila desde bdonde todas las columnas del mismo nombre contienen los mismos valores. No necesitamos una actualización en este caso (nada cambia) y podemos eliminar esas filas al principio del proceso ( WHERE b.id IS NULL).
Todavía necesitamos encontrar una fila coincidente, así que b.id = ab.iden la consulta externa.

db <> violín aquí
Viejo sqlfiddle.

Este es SQL estándar, excepto por la FROMcláusula .
Funciona sin importar en qué columnas estén realmente presentes A, pero la consulta no puede distinguir entre valores NULL reales y columnas faltantes A, por lo que solo es confiable si todas las columnas Aestán definidas NOT NULL.

Existen múltiples variaciones posibles, según lo que sepa sobre ambas tablas.

Erwin Brandstetter
fuente
El poder de SQL! Acabo de notar cuando agrega paréntesis en la cláusula set ( SET (column1) = (a.column)) Postgres lo tratará como otro tipo de actualización y dará un error como este:source for a multiple-column UPDATE item must be a sub-SELECT or ROW() expression
Edgar Ortega
26

He estado trabajando con la base de datos IBM DB2 durante más de una década y ahora estoy tratando de aprender PostgreSQL.

Funciona en PostgreSQL 9.3.4, pero no funciona en DB2 10.5:

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

Nota: El problema principal es DE, porque no es compatible con DB2 y tampoco con ANSI SQL.

Funciona en DB2 10.5, pero NO funciona en PostgreSQL 9.3.4:

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

¡FINALMENTE! Funciona tanto en PostgreSQL 9.3.4 como en 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
fuente
3
Tenga en cuenta que las consultas segunda y tercera no son completamente equivalentes a la primera. Si no se encuentra una fila coincidente B, la primera instrucción no hace nada (la fila original permanece intacta), mientras que las otras dos columnas de sobrescritura con valores NULL.
Erwin Brandstetter
7

Esta es una gran ayuda. El 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 perfectamente.

observó que necesita un soporte "" en

From "tbl_a" a

para que funcione

usuario2493970
fuente
5

No necesariamente lo que pediste, pero ¿quizás podría ser útil usar la herencia de Postgres?

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

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

Esto evita la necesidad de actualizar B.

Pero asegúrese de leer todos los detalles .

De lo contrario, lo que solicita no se considera una buena práctica: SELECT * ...se desaconsejan las cosas dinámicas como las vistas con (ya que una leve conveniencia podría romper más cosas que ayudar a las cosas), y lo que pide sería equivalente al UPDATE ... SETcomando.

Insensatez
fuente
No estoy seguro de cómo la herencia resolverá esto. ¿Te refieres a agregar un activador de actualización para A que también actualice B? No quiero sincronizar A con B todo el tiempo, solo a pedido. Y en tal caso, no puedo usar los desencadenantes.
Nir
2
Sí, si es solo en ciertos casos, la herencia no funcionaría y en ese caso desaconsejo el enfoque de consulta dinámica. (todavía hay formas de lograr esto usando lenguajes de procedimiento de postgres. también si desea usar disparadores, también puede usarlos, agregando un campo de sincronización, por ejemplo, disparando el disparador solo cuando está configurado).
razón
0

puedes construir y ejecutar sql dinámico para hacer esto, pero realmente no es lo ideal

Daniel Brink
fuente
Pensé en eso. Pensé que podría hacer que mi consulta fuera compatible con los cambios posteriores en ambas tablas, pero sql dinámico parece ser demasiado complicado que solo especificar todos los campos y olvidarse de la compatibilidad hacia adelante.
Nir
Sí, será complicado, pero debe ser compatible con versiones posteriores que se agreguen o eliminen. Primero tendrá que hacer una consulta para obtener los nombres de columna de ambas tablas, luego hacer coincidir los nombres de columna y luego escribir el sql dinámico para hacer la actualización en función de los nombres de columna coincidentes. un proyecto divertido en realidad :)
Daniel Brink
-4

Intenta seguir

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

EDITADO: - Actualice más de una columna

Update A a, B b, SET a.column1=b.column1, a.column2=b.column2 where b.id=1
Salil
fuente
No entiendo cómo copia column1, column2 y column3. Y necesito mencionar explícitamente column1.
Nir
No funciona para mi Me sale el siguiente error: ERROR: error de sintaxis en o cerca de ","
melbic
1
Esta sintaxis no estándar funcionaría UPDATEen MySQL , pero no es válida para PostgreSQL.
Erwin Brandstetter