如何在PostgreSQL中进行更新+加入?


506

基本上,我想这样做:

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中编写此代码?



4
车辆_车辆,装运_装运?这是一个有趣的表命名约定
CodeAndCats

3
@CodeAndCats哈哈...看起来确实很有趣,不是吗?我想我当时使用的是Django,并且表格按功能分组。因此,将有一个视图vehicles_*表和一些shipments_*表。
mpen

Answers:


775

UPDATE语法是:

[WITH [RECURSIVE] with_query [,...]]
更新[仅]表[[AS]别名]
    SET {列= {表达式| 默认} |
          (column [,...])=({表达式| DEFAULT} [,...])} [,...]
    [FROM from_list]
    [状况| cursor_name的当前位置]
    [返回* | 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 

如果更新依赖于表联接的整个列表,那么这些联接应该在UPDATE节还是FROM节中?
ted.strauss 2012年

11
@ ted.strauss:FROM可以包含表列表。
Mark Byers

140

让我以我的例子作更多解释。

任务:正确的信息,即有待进取的人(即将离开中学的学生)比他们获得学校证书的时间更早提交申请(是的,他们比获得证书的时间更早(按指定的证书日期)获得证书。所以,我们将增加申请提交日期以适合证书签发日期。

从而。下一个类似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

如您所见,原始子查询JOINON子句已成为WHERE条件之一,AND与其他条件结合在一起,这些条件已从子查询中移出,没有任何更改。而且,不再需要JOIN使用自身来表(就像在子查询中一样)。


16
您将如何加入第三张桌子?
咆哮者

19
JOIN可以像往常一样在FROM列表中:FROM abiturients b JOIN addresses c ON c.abiturient_id = b.id
Envek '17

@Envek-just,我不能在其中使用JOIN。postgresql.org/docs/10/static/sql-update.html
阿德里安·史密斯

3
@AdrianSmith,您不能在UPDATE本身中使用JOIN,但可以在UPDATE的from_list子句(PostgreSQL的SQL扩展)中使用它。另外,请参阅提供的链接上有关加入表注意事项的注释。
Envek '17

@Envek-啊,谢谢您的澄清,我错过了。
阿德里安·史密斯

130

在这种情况下,马克·拜尔斯的答案是最佳的。尽管在更复杂的情况下,您可以采用返回行号和计算值的选择查询,并将其附加到更新查询中,如下所示:

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 table1
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转储次数。:)
dannysauer 2015年

5
不知道为什么,但是此查询的CTE版本比上面的“普通
联接

该解决方案的另一个优点是可以通过使用with / select语句中的多个联接,从两个以上的表联接以获得最终的计算值。
Alex Muro

1
这太棒了。我精挑细选了自己,就像@dannysauer一样,我害怕转换。这就是为我做的。完善!
frostymarvelous

1
您的第一个SQL示例具有语法错误。“ update t1”不能使用t子查询中的别名,它需要使用表名:“ update table1”。在第二个示例中,您可以正确执行此操作。
EricS

79

对于真正想要做的人,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;

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


4
考虑到这是带有实际连接的第一个答案(而不是带子查询的内部答案),因此这应该是真正接受的答案。应该重命名该问题,以免混淆postgresql是否支持update中的联接。
项链

有用。但是,感觉就像一个黑客
M.哈比卜

应该注意的是,根据文档(postgresql.org/docs/11/sql-update.html),在from子句中列出目标表将导致目标表自连接。不太自信,在我看来,这是一个交叉自我联接,可能会产生意想不到的结果和/或性能影响。
Ben Collins

12

对于那些想要执行JOIN的用户,只更新您的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
--the below line is critical for updating ONLY joined rows
AND a.pk_id = a_alias.pk_id;

上面已经提到了这一点,但仅是通过注释。。因为获得正确结果并发布有效的新答案至关重要


7

开始了:

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

我能做到的很简单。谢谢你们!

也可以这样做:

-- 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;

但是随后您在那儿有两次车辆表,并且只允许对其别名一次,并且您不能在“设置”部分中使用别名。


@littlegreen您确定吗?不是join约束吗?
mpen 2015年

4
@mpen我可以确认它会将所有记录更新为一个值。它并没有达到您的期望。
亚当·贝尔

1

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

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


0

第一表名称:tbl_table1(tab1)。第二个表名称:tbl_table2(tab2)。

将tbl_table1的ac_status列设置为“ INACTIVE”

update common.tbl_table1 as tab1
set ac_status= 'INACTIVE' --tbl_table1's "ac_status"
from common.tbl_table2 as tab2
where tab1.ref_id= '1111111' 
and tab2.rel_type= 'CUSTOMER';
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.