web-dev-qa-db-fra.com

Comment faire une mise à jour + rejoindre dans PostgreSQL?

En gros, je veux faire ceci:

update vehicles_vehicle v 
    join shipments_shipment s on v.shipment_id=s.id 
set v.price=s.price_per_vehicle;

Je suis sûr que cela fonctionnerait avec MySQL (mon expérience), mais cela ne semble pas fonctionner avec postgres. L'erreur que je reçois est:

ERROR:  syntax error at or near "join"
LINE 1: update vehicles_vehicle v join shipments_shipment s on v.shi...
                                  ^

Il existe sûrement un moyen facile de le faire, mais je ne trouve pas la syntaxe appropriée. Alors, comment pourrais-je écrire cela dans PostgreSQL?

396
mpen

La syntaxe UPDATE est la suivante:

 [WITH [RECURSIVE] with_query [ ...]] 
 UPDATE [ONLY] table [alias [[AS]]] 
 SET {colonne = {expression | DEFAULT} | 
 (colonne [ ...]) = ({expression | DEFAULT} [ ...])} [ ...] 
 [FROM from_list] 
 [OERE condition | WHERE CURRENT OF nom_curseur] 
 [RETOURNANT * | expression_sortie [[AS] nom_sortie] [ ...]] 

Dans votre cas, je pense que vous voulez ceci:

UPDATE vehicles_vehicle AS v 
SET price = s.price_per_vehicle
FROM shipments_shipment AS s
WHERE v.shipment_id = s.id 
641
Mark Byers

Laissez-moi vous expliquer un peu plus par mon exemple.

Tâche: informations correctes, où les élèves (les étudiants sur le point de quitter l’école secondaire) ont soumis leurs demandes à l’université plus tôt, qu’ils ont obtenu des certificats d’études (oui, ils ont obtenu des certificats plus tôt que ce qu’ils ont été délivrés (date spécifiée)). augmenter la date de soumission de la demande pour correspondre à la date de délivrance du certificat. 

Ainsi. prochaine déclaration de type MySQL:

UPDATE applications a
JOIN (
    SELECT ap.id, ab.certificate_issued_at
    FROM abiturients ab
    JOIN applications ap 
    ON ab.id = ap.abiturient_id 
    WHERE ap.documents_taken_at::date < ab.certificate_issued_at
) b
ON a.id = b.id
SET a.documents_taken_at = b.certificate_issued_at;

Devient comme PostgreSQL

UPDATE applications a
SET documents_taken_at = b.certificate_issued_at         -- we can reference joined table here
FROM abiturients b                                       -- joined table
WHERE 
    a.abiturient_id = b.id AND                           -- JOIN ON clause
    a.documents_taken_at::date < b.certificate_issued_at -- Subquery WHERE

Comme vous pouvez le constater, la clause JOIN de la sous-requête originale ON est devenue l’une des conditions WHERE, qui est créée par AND avec d’autres, qui ont été déplacées de la sous-requête sans modification. Et il n'y a plus besoin de JOIN table avec lui-même (comme c'était le cas dans la sous-requête).

108
Envek

La réponse de Mark Byers est la solution optimale dans cette situation. Bien que dans des situations plus complexes, vous pouvez prendre la requête select qui renvoie les ID de ligne et les valeurs calculées et la lier à la requête de mise à jour comme suit:

with t as (
  -- Any generic query which returns rowid and corresponding calculated values
  select t1.id as rowid, f(t2, t2) as calculatedvalue
  from table1 as t1
  join table2 as t2 on t2.referenceid = t1.id
)
update t1
set value = t.calculatedvalue
from t
where id = t.rowid

Cette approche vous permet de développer et de tester votre requête sélectionnée et, en deux étapes, de la convertir en requête de mise à jour.

Donc, dans votre cas, la requête de résultat sera:

with t as (
    select v.id as rowid, s.price_per_vehicle as calculatedvalue
    from vehicles_vehicle v 
    join shipments_shipment s on v.shipment_id = s.id 
)
update vehicles_vehicle
set price = t.calculatedvalue
from t
where id = t.rowid

Notez que les alias de colonnes sont obligatoires sinon PostgreSQL se plaindra de l'ambiguïté des noms de colonnes.

96
Alvin

Pour ceux qui veulent réellement faire une JOIN, vous pouvez également utiliser:

UPDATE a
SET price = b_alias.unit_price
FROM      a AS a_alias
LEFT JOIN b AS b_alias ON a_alias.b_fk = b_alias.id
WHERE a_alias.unit_name LIKE 'some_value' 
AND a.id = a_alias.id;

Vous pouvez utiliser a_alias dans la section SET à droite du signe égal, si nécessaire . Les champs situés à gauche du signe égal ne nécessitent pas de référence à une table car ils sont réputés provenir de l'original "a". table.

45
Fast Engy

Et c'est parti:

update vehicles_vehicle v
set price=s.price_per_vehicle
from shipments_shipment s
where v.shipment_id=s.id;

Aussi simple que je pouvais le faire. Merci les gars!

Peut aussi faire ceci:

update vehicles_vehicle 
set price=s.price_per_vehicle
from vehicles_vehicle v
join shipments_shipment s on v.shipment_id=s.id;

Mais ensuite, vous avez la table de véhicules deux fois, et vous n'êtes autorisé à la nommer qu'une fois, et vous ne pouvez pas utiliser l'alias dans la partie "set".

5
mpen

Pour ceux qui souhaitent faire une jointure mettant à jour UNIQUEMENT les lignes que votre jointure retourne, utilisez:

UPDATE a
SET price = b_alias.unit_price
FROM      a AS a_alias
LEFT JOIN b AS b_alias ON a_alias.b_fk = b_alias.id
WHERE a_alias.unit_name LIKE 'some_value' 
AND a.id = a_alias.id
--the below line is critical for updating ONLY joined rows
AND a.pk_id = a_alias.pk_id;

Cela a été mentionné ci-dessus, mais uniquement par le biais d'un commentaire. Étant donné qu'il est essentiel d'obtenir le résultat correct en affichant une NOUVELLE réponse qui fonctionne.

2
Nate Smith

Le lien ci-dessous contient un exemple qui résout et aide à comprendre mieux comment utiliser update et join avec postgres.

UPDATE product
SET net_price = price - price * discount
FROM
product_segment
WHERE
product.segment_id = product_segment.id;

Voir: http://www.postgresqltutorial.com/postgresql-update-join/

0
Alessandro

Voici un SQL simple qui met à jour Mid_Name sur la table Name3 à l'aide du champ Middle_Name de Name:

update name3
set mid_name = name.middle_name
from name
where name3.person_id = name.person_id;
0
Yusuf