关于语法:如何在PostgreSQL中进行更新+加入?

How to do an update + join in PostgreSQL?

基本上,我想这样做:

1
2
3
UPDATE vehicles_vehicle v
    JOIN shipments_shipment s ON v.shipment_id=s.id
SET v.price=s.price_per_vehicle;

我很确定它可以在MySQL(我的背景)中工作,但它似乎不适用于postgres。 我得到的错误是:

1
2
3
ERROR:  syntax error at OR near"join"
LINE 1: UPDATE vehicles_vehicle v JOIN shipments_shipment s ON v.shi...
                                  ^

当然有一种简单的方法可以做到这一点,但我找不到合适的语法。 那么,我如何在PostgreSQL中编写这个?


UPDATE语法是:

1
2
3
4
5
6
7
[ 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 ] [, ...] ]

在你的情况下,我认为你想要这个:

1
2
3
4
UPDATE vehicles_vehicle AS v
SET price = s.price_per_vehicle
FROM shipments_shipment AS s
WHERE v.shipment_id = s.id


让我通过我的例子再解释一下。

任务:正确的信息,其中abiturients(即将离开中学的学生)早些时候向大学提交申请,而不是他们获得学校证书(是的,他们获得的证书早于他们颁发的证书(通过指定的证书日期)。所以,我们将增加申请提交日期以适应证书签发日期。

从而。下一个类似MySQL的声明:

1
2
3
4
5
6
7
8
9
10
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的

1
2
3
4
5
6
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

正如您所看到的,原始子查询JOINON子句已成为WHERE条件之一,由AND与其他条件结合,这些条件已从子查询中移除而没有更改。并且不再需要JOIN表自身(就像在子查询中一样)。


Mark Byers的答案在这种情况下是最佳的。
虽然在更复杂的情况下,您可以使用返回rowid和计算值的select查询,并将其附加到更新查询,如下所示:

1
2
3
4
5
6
7
8
9
10
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

此方法允许您开发和测试您的选择查询,并分两步将其转换为更新查询。

因此,在您的情况下,结果查询将是:

1
2
3
4
5
6
7
8
9
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会抱怨列名的含糊不清。


对于那些真正想要做JOIN的人,你也可以使用:

1
2
3
4
5
6
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;

如果需要,您可以在等号右侧的SET部分中使用a_alias。
等号左侧的字段不需要表引用,因为它们被认为来自原始的"a"表。


对于那些想要进行JOIN的人来说,只更新你的联接返回的行:

1
2
3
4
5
6
7
8
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;

这是上面提到的,但只是通过评论..因为获得正确的结果发布新的答案,这是至关重要的


开始了:

1
2
3
4
UPDATE vehicles_vehicle v
SET price=s.price_per_vehicle
FROM shipments_shipment s
WHERE v.shipment_id=s.id;

简单,因为我可以做到。多谢你们!

也可以这样做:

1
2
3
4
5
-- Doesn't work apparently
UPDATE vehicles_vehicle
SET price=s.price_per_vehicle
FROM vehicles_vehicle v
JOIN shipments_shipment s ON v.shipment_id=s.id;

但是那时你已经有两次车辆表,并且你只允许别名一次,你不能在"set"部分使用别名。


下面的链接有一个示例可以解决并帮助更好地理解如何使用postgres updateJOIN

1
2
3
4
5
6
UPDATE product
SET net_price = price - price * discount
FROM
product_segment
WHERE
product.segment_id = product_segment.id;

请参阅:http://www.postgresqltutorial.com/postgresql-update-join/


这是一个简单的SQL,它使用Name中的Middle_Name字段更新Name3表上的Mid_Name:

1
2
3
4
UPDATE name3
SET mid_name = name.middle_name
FROM name
WHERE name3.person_id = name.person_id;