Как сделать обновление + присоединиться в PostgreSQL?

в принципе, я хочу сделать это:

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

Я уверен, что это будет работать в MySQL (мой фон), но он, похоже, не работает в postgres. Ошибка, которую я получаю:

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

конечно, есть простой способ сделать это, но я не могу найти правильный синтаксис. Итак, как бы я написал это в PostgreSQL?

6 ответов


на обновить синтаксис - это:

[ WITH [ RECURSIVE ] with_query [, ...] ]
UPDATE [ ONLY ] table [ [ AS ] alias ]
    SET { column = { expression | DEFAULT } |
          ( column [, ...] ) = ( { expression | DEFAULT } [, ...] ) } [, ...]
    [ FROM from_list ]
    [ WHERE condition | WHERE CURRENT OF cursor_name ]
    [ RETURNING * | output_expression [ [ AS ] output_name ] [, ...] ]

в вашем случае, я думаю, вы хотите этого:

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

позвольте мне объяснить немного больше на моем примере.

задача: правильная информация, где абитуриенты (студенты, собирающиеся покинуть среднюю школу) подали заявки в университет раньше, чем они получили школьные сертификаты (да, они получили сертификаты раньше, чем они были выданы (по дате сертификата указано). Таким образом, мы увеличим дату подачи заявки, чтобы соответствовать дате выдачи сертификата.

таким образом. следующий 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;

будет 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

как вы можете видеть, оригинальный подзапрос JOIN ' s ON пункт стали одним из WHERE условия, которые conjucted по AND С другими, которые были перемещены из подзапроса без изменения. И больше нет необходимости JOIN таблицы с самой собой (как это было в подзапросе).


ответ Марка Байерса является оптимальным в этой ситуации. Хотя в более сложных ситуациях вы можете взять запрос select, который возвращает rowids и вычисленные значения, и прикрепить его к запросу update следующим образом:

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

этот подход позволяет разработать и протестировать запрос select и в два шага преобразовать его в запрос update.

поэтому в вашем случае запрос результата будет:

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

обратите внимание, что псевдонимы столбцов обязательны в противном случае PostgreSQL будет жаловаться на неоднозначность имен столбцов.


для тех, кто действительно хочет присоединиться, вы также можете использовать:

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;

при необходимости вы можете использовать a_alias в разделе SET справа от знака равенства. Поля слева от знака равенства не требуют ссылки на таблицу, поскольку они считаются исходными из таблицы "a".


поехали:

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

просто, как я мог сделать это. Спасибо, ребята!

может также сделать это:

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

но тогда у вас есть таблица транспортных средств там дважды, и Вам разрешено псевдоним только один раз, и вы не можете использовать псевдоним в части "set".


вот простой SQL, который обновляет Mid_Name в таблице Name3, используя поле Middle_Name из Name:

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