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

392

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

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?

  • 3
    Синтаксис Postgres отличается: postgresql.org/docs/8.1/static/sql-update.html
  • 2
    транспортные средства, грузоперевозки, доставка? Это интересное соглашение об именах таблиц
Показать ещё 1 комментарий
Теги:
syntax

7 ответов

631
Лучший ответ

Синтаксис UPDATE:

[ 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 
  • 0
    Если обновление опирается на целый список объединений таблиц, должны ли они быть в разделе ОБНОВЛЕНИЕ или в разделе ОТ?
  • 6
    @ ted.strauss: FROM может содержать список таблиц.
105

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

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

Таким образом. следующий 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 ON стал одним из условий WHERE, который соединен с AND с другими, которые были перемещены из подзапроса без изменений. И больше нет необходимости в таблице JOIN с самим собой (как это было в подзапросе).

  • 9
    Как бы вы присоединились к третьему столу?
  • 9
    Вы просто JOIN как обычно в FROM списке: FROM abiturients b JOIN addresses c ON c.abiturient_id = b.id
Показать ещё 4 комментария
90

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

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

Этот подход позволяет вам разрабатывать и тестировать ваш запрос выбора и в два этапа преобразовывать его в запрос обновления.

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

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 будет жаловаться на неоднозначность имен столбцов.

  • 1
    Мне очень нравится этот, потому что я всегда немного нервничаю, когда беру свой «выбор» сверху и заменяю его «обновлением», особенно с несколькими объединениями. Это уменьшает количество дампов SQL, которые я должен сделать перед массовыми обновлениями. :)
  • 4
    Не знаю почему, но версия этого запроса для CTE намного быстрее, чем приведенные выше решения для простого соединения.
Показать ещё 4 комментария
40

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

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 справа от знака равенства. Поля слева от знака равенства не требуют ссылки на таблицу, так как они считаются исходной таблицей "а".

  • 15
    Чтобы это работало, мне пришлось добавить: AND a.pk_id = a_alias.pk_id
5

Здесь мы идем:

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".

  • 6
    Вторая версия обновляет ВСЕ записи в таблице, повреждая ее ...
  • 0
    @littlegreen Ты уверен в этом? Разве join ограничивает это?
Показать ещё 1 комментарий
0

Для тех, кто хочет создать JOIN, который обновляет ТОЛЬКО строки, которые использует ваше соединение:

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
AND a.pk_id = a_alias.pk_id;

Это было упомянуто выше, но только через комментарий. Поскольку очень важно получить правильный результат, публикуя НОВЫЙ ответ, который работает

0

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню