2017-03-05 3 views
1

Cette question a été posée plusieurs fois sur stackoverflow, mais ils semblaient tous avoir plus d'un an donc je me suis dit que je demanderais à nouveau au cas où il y aurait eu une mise à jour.Mise à jour corrélée SQLAlchemy avec plusieurs colonnes?

correlated update est une instruction de mise à jour qui met à jour toutes les lignes d'une table en fonction des valeurs d'une autre table, tout en reliant les deux tables.

De l'SQLAlchemy docs, nous pouvons faire une mise à jour corrélée facilement, mais seulement sur une seule colonne:

update(foo).values(bar=select([foobar.c.bar]).where(foobar.c.id == foo.c.id)) 

Cela se traduit par:

UPDATE foo 
SET bar = (
    SELECT bar 
    FROM foobar 
    WHERE foobar.id = foo.id 
) 

Comment peut-on écrire une mise à jour connexes en utilisant plus de une colonne dans sqlalchemy? Par exemple:

UPDATE foo 
SET (bar, baz) = (
    SELECT bar, baz 
    FROM foobar 
    WHERE foobar.id = foo.id 
) 

Répondre

2

Selon votre avatar et votre description, je suppose que vous utilisez Oracle. De this answer un dispositif peut la concotion SQLAlchemy suivante, si vos résultats de jointure dans un key preserved view:

stmt = select([foo.c.bar.label('foo_bar'), 
       foo.c.baz.label('foo_baz'), 
       foobar.c.bar.label('foobar_bar'), 
       foobar.c.baz.label('foobar_baz')]).\ 
    where(foo.c.id == foobar.c.id) 

update(stmt).values({stmt.c.foo_bar: stmt.c.foobar_bar, 
        stmt.c.foo_baz: stmt.c.foobar_baz}) 

qui produit l'instruction SQL suivante:

UPDATE (SELECT foo.bar AS foo_bar, 
       foo.baz AS foo_baz, 
       foobar.bar AS foobar_bar, 
       foobar.baz AS foobar_baz 
     FROM foo, foobar 
     WHERE foo.id = foobar.id) 
SET foo_bar=foobar_bar, foo_baz=foobar_baz 

Les étiquettes sont importantes, car vos tables partagent les noms de colonnes.

Vous pouvez également produire votre SQL cible d'origine:

from sqlalchemy import tuple_, select, exists 

stmt = select([foobar.c.bar, foobar.c.baz]).where(foo.c.id == foobar.c.id) 
foo.update().\ 
    values({tuple_(foo.c.bar, foo.c.baz).self_group(): stmt}).\ 
    where(exists(stmt)) 

L'appel self_group() est important, car le compilateur semble omettre les parenthèses autour du tuple, produisant une syntaxe incorrecte, dans ce cas. J'ai ajouté la clause WHERE afin d'éviter la mise à jour foo lignes sans correspondance foobar:

UPDATE foo SET (bar, baz)=(SELECT foobar.bar, foobar.baz 
FROM foobar 
WHERE foo.id = foobar.id) WHERE EXISTS (SELECT foobar.bar, foobar.baz 
FROM foobar 
WHERE foo.id = foobar.id) 
+0

Vous êtes un héros; Je vous remercie! Je vous demanderais de spammer cette réponse sur chaque question de 'mise à jour corrélée avec plusieurs colonnes' que vous pouvez trouver! –

+0

Y a-t-il un avantage à utiliser votre première approche par rapport à la deuxième approche? –

+0

Je suppose que ne pas avoir à faire le test EXISTS séparé afin de ne pas mettre à jour les lignes inégalées dans la 1ère approche est un avantage par rapport à la 2ème. Bien que la première approche puisse mieux prendre en charge les vues jointes complexes, elle est limitée par [préservation de clé] (http://docs.oracle.com/cd/E11882_01/server.112/e25494/views.htm#ADMIN11783). Cela pourrait ou non vous forcer à opter pour la deuxième approche dans certains cas. J'ai très peu ou pas d'expérience avec Oracle, donc je vais devoir laisser les détails au lecteur. –