179 votes

Mise à jour SQL des champs d'une table à partir des champs d'une autre table

J'ai deux tableaux :

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

A sera toujours un sous-ensemble de B (c'est-à-dire toutes les colonnes de A sont également en B ).

Je souhaite mettre à jour un enregistrement avec un ID en B avec leurs données de A pour toutes les colonnes de A . Cette ID existe à la fois dans A y B .

Existe-t-il un UPDATE ou tout autre moyen de le faire sans spécifier les noms des colonnes, en disant simplement "définir toutes les colonnes de A" ?

J'utilise PostgreSQL, donc une commande spécifique non standard est également acceptée (mais pas préférée).

352voto

Scott Bailey Points 2094

Vous pouvez utiliser l'option non standard DE clause.

UPDATE b
SET column1 = a.column1,
  column2 = a.column2,
  column3 = a.column3
FROM a
WHERE a.id = b.id
AND b.id = 1

63voto

Erwin Brandstetter Points 110228

La question est ancienne, mais j'ai eu le sentiment que la meilleure réponse n'avait pas encore été donnée.

Existe-t-il un UPDATE syntaxe ... sans spécifier les noms des colonnes ?

Solution générale avec SQL dynamique

Connaissant uniquement la colonne de la clé primaire des deux tables

Vous n'avez pas besoin de connaître le nom des colonnes, à l'exception d'une ou plusieurs colonnes uniques sur lesquelles effectuer la jointure ( id dans l'exemple). Ce système fonctionne de manière fiable dans tous les cas de figure auxquels je peux penser.

Ceci est spécifique à PostgreSQL. Je suis en train de construire un code dynamique basé sur le information_schema , en particulier le tableau information_schema.columns qui est définie dans la norme SQL et que la plupart des principaux SGBDR (à l'exception d'Oracle) possèdent. Mais un DO déclaration avec PL/pgSQL qui exécute du code SQL dynamique est une syntaxe totalement non standard de 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$;

En supposant qu'il existe une colonne correspondante dans b pour tous colonne dans a mais pas l'inverse. b peut comporter des colonnes supplémentaires.

WHERE b.id = 123 est facultatif, pour mettre à jour une ligne sélectionnée.

_db<>fiddle aquí_
Ancienne <a href="http://sqlfiddle.com/#!17/cdd2b/3" rel="nofollow noreferrer">sqlfiddle</a>

Réponses connexes avec plus d'explications :

Solutions partielles avec SQL simple

Connaître la liste des colonnes partagées

Vous connaissez la liste des noms de colonnes que les deux tables partagent. Avec un raccourci syntaxique pour la mise à jour de plusieurs colonnes - plus court que ce que les autres réponses ont suggéré jusqu'à présent dans tous les cas.

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;

_db<>fiddle aquí_
Ancienne <a href="http://sqlfiddle.com/#!17/cdd2b/3" rel="nofollow noreferrer">sqlfiddle</a>

Cette syntaxe a été introduite avec Postgres 8.2 en 2006, bien avant que la question ne soit posée. Détails dans le manuel.

En rapport :

Connaître la liste des colonnes dans B

Si toutes les colonnes de A sont définis NOT NULL (mais pas nécessairement toutes les colonnes de B ),
et vous connaissez les noms des colonnes de B (mais pas nécessairement ceux de 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;

Les NATURAL LEFT JOIN joint une ligne de b où toutes les colonnes du même nom contiennent les mêmes valeurs. Nous n'avons pas besoin d'une mise à jour dans ce cas (rien ne change) et nous pouvons éliminer ces lignes dès le début du processus ( WHERE b.id IS NULL ).
Nous devons encore trouver une ligne correspondante, donc b.id = ab.id dans la requête extérieure.

_db<>fiddle aquí_
Ancienne <a href="http://sqlfiddle.com/#!15/62fce/1" rel="nofollow noreferrer">sqlfiddle</a>

Il s'agit d'un code SQL standard à l'exception de la FROM clause .
Cela fonctionne quelle que soit la colonne présente dans le fichier A mais la requête ne peut pas faire la distinction entre les valeurs NULL réelles et les colonnes manquantes dans la base de données des A Il n'est donc fiable que si toutes les colonnes de A sont définis NOT NULL .

Il existe de multiples variantes possibles, en fonction de ce que vous conozca sur les deux tables.

35voto

jochan Points 339

Je travaille avec la base de données IBM DB2 depuis plus de dix ans et j'essaie maintenant d'apprendre PostgreSQL.

Il fonctionne sur PostgreSQL 9.3.4, mais pas sur DB2 10.5 :

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

Note : Le problème principal est la cause FROM qui n'est pas supportée par DB2 et pas non plus par ANSI SQL.

Il fonctionne sur DB2 10.5, mais ne fonctionne pas sur PostgreSQL 9.3.4 :

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

ENFIN ! Il fonctionne à la fois sur PostgreSQL 9.3.4 et 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)

9voto

user2493970 Points 21

C'est une aide précieuse. Le code

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;

fonctionne parfaitement.

a noté que vous avez besoin d'une parenthèse "" dans

From "tbl_a" a

pour le faire fonctionner.

6voto

Unreason Points 8703

Ce n'est pas nécessairement ce que vous avez demandé, mais l'utilisation de l'héritage postgres pourrait peut-être vous aider.

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

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

Il n'est donc pas nécessaire de mettre à jour B.

Mais ne manquez pas de lire tous les détails .

Sinon, ce que vous demandez n'est pas considéré comme une bonne pratique - les éléments dynamiques tels que les vues avec des SELECT * ... sont déconseillés (car une telle légère commodité pourrait casser plus de choses que d'en améliorer), et ce que vous demandez serait équivalent pour l'option UPDATE ... SET commande.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X