web-dev-qa-db-fra.com

Les champs de mise à jour SQL d'une table à partir des champs d'une autre

J'ai deux tables:

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

A sera toujours un sous-ensemble de B (ce qui signifie que toutes les colonnes de A sont également dans B).

Je souhaite mettre à jour un enregistrement avec un ID spécifique dans B avec ses données provenant de A pour toutes les colonnes de A. Ce ID existe à la fois dans A et B.

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

J'utilise PostgreSQL, donc une commande non standard spécifique est également acceptée (toutefois, ce n'est pas préférable).

111
Nir

Vous pouvez utiliser la clause non standard À PARTIR DE .

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

La question est ancienne mais je sentais que la meilleure réponse n’avait pas encore été donnée.

Existe-t-il une syntaxe UPDATE ... sans spécifier les noms de colonne ?

Solution générale avec SQL dynamique

Vous n'avez pas besoin de connaître les noms de colonne, à l'exception de quelques colonnes uniques à joindre (id dans l'exemple). . Fonctionne de manière fiable pour tous les cas possibles d'angle auxquels je peux penser.

Ceci est spécifique à PostgreSQL. Je construis un code dynamique basé sur le information_schema , en particulier la table information_schema.columns , défini dans ANSI SQL et pris en charge par les SGBDR les plus modernes (sauf Oracle). Cependant, une instruction DO avec PL/pgSQL qui exécute du code SQL dynamique est une syntaxe PostgreSQL totalement non standard.

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 une colonne correspondante dans b pour tous colonne dans a, mais pas l'inverse. b peut avoir des colonnes supplémentaires.

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

SQL Fiddle.

Réponses associées avec plus d'explications:

Solutions partielles avec du SQL simple

Avec liste de colonnes partagées

Vous devez toujours connaître la liste des noms de colonnes que partagent les deux tables. Avec un raccourci de syntaxe pour mettre à jour plusieurs colonnes - plus court que ce que d’autres réponses suggéraient 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;

SQL Fiddle.

Cette syntaxe a été introduite dans Postgres 8.2 en décembre 2006, bien avant que la question ne soit posée.
Plus de détails dans le manuel et cette réponse sur dba.SE:

Avec liste de colonnes dans B

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

Le NATURAL LEFT JOIN joint une ligne à partir de b où toutes les colonnes du même nom contiennent les mêmes valeurs. Dans ce cas, nous n’avons pas besoin de mise à jour (rien ne change) et pouvons éliminer ces lignes plus tôt dans le processus (WHERE b.id IS NULL).
Nous avons encore besoin de trouver une rangée correspondante, donc b.id = ab.id dans la requête externe.

SQL Fiddle.

C'est le SQL standard sauf pour la clause FROM .
Cela fonctionne quelle que soit la colonne réellement présente dans A, mais la requête ne permet pas de distinguer les valeurs NULL réelles des colonnes manquantes dans A. Elle n'est donc fiable que si toutes les colonnes de A sont définies NOT NULL.

Il existe plusieurs variantes possibles, selon ce que vous savez à propos des deux tables.

42
Erwin Brandstetter

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

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

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

Remarque: Le problème principal est une cause FROM qui n'est pas prise en charge dans DB2 et également dans ANSI SQL.

Cela 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)

FINALEMENT! Cela 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)
23
jochan

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'un support "" dans

From "tbl_a" a

pour le faire fonctionner.

7
user2493970

Pas nécessairement ce que vous avez demandé, mais peut-être que l'utilisation de l'héritage postgres pourrait vous aider?

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

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

Cela évite d'avoir à mettre à jour B.

Mais assurez-vous de lire tous les détails .

Sinon, ce que vous demandez n'est pas considéré comme une bonne pratique - des éléments dynamiques tels que des vues avec SELECT * ... sont découragés (une telle commodité pourrait casser plus de choses que d’aider), et ce que vous demanderiez serait équivalent pour le UPDATE ... SET commande.

5
Unreason

vous pouvez construire et exécuter SQL dynamique pour le faire, mais ce n'est vraiment pas idéal

0
Daniel Brink