【问题标题】:How to do an update + join in PostgreSQL?如何在 PostgreSQL 中进行更新 + 加入?
【发布时间】:2011-12-13 18:02:07
【问题描述】:

基本上,我想这样做:

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 中写这个呢?

【问题讨论】:

  • vehicles_vehicle, shipping_shipment?这是一个有趣的表命名约定
  • @CodeAndCats 哈哈...看起来很有趣不是吗?我想我当时正在使用 Django,并且这些表是按功能分组的。所以会有一个视图vehicles_* 表和一些shipments_* 表。

标签: postgresql join sql-update


【解决方案1】:

UPDATE syntax 是:

[ WITH [ RECURSIVE ] with_query [, ...] ] 更新 [ONLY] 表 [[AS] 别名] SET { 列 = { 表达式 |默认 } | ( 列 [, ...] ) = ( { 表达式 | DEFAULT } [, ...] ) } [, ...] [ 来自 from_list ] [ WHERE 条件 |游标名称的当前位置] [返回* |输出表达式 [ [ AS ] 输出名称 ] [, ...] ]

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

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:FROM 可以包含表格列表。
  • 来自 mysql 它是不直观的,用于 select 的相同连接不会也 update 仅仅通过添加一个 set 短语 :( 仍然 - 这个语法可能更容易sql新手到master。
  • @WEBjuju 我的想法完全正确,将选择语句转换为更新需要使用此方法的额外步骤,这很不方便。这种方式的语法也不太直观(在我看来)。
  • 更新行中的别名出错;我删除了它,没有错误。
【解决方案2】:

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

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 会抱怨列名的歧义。

【讨论】:

  • 我真的很喜欢这个,因为我总是有点紧张,将我的“选择”从顶部删除并用“更新”替换它,尤其是多个连接。这减少了在大规模更新之前我应该​​做的 SQL 转储的数量。 :)
  • 不知道为什么,但是这个查询的 CTE 版本比上面的“普通连接”解决方案要快得多
  • 此解决方案的另一个优点是能够通过在 with / select 语句中使用多个连接来从两个以上的表连接以获得最终计算值。
  • 这太棒了。我精心挑选了我的选择,就像@dannysauer 一样,我害怕转换。这只是为我做的。完美!
  • 您的第一个 SQL 示例有语法错误。 “update t1”不能使用来自 t 子查询的别名,它需要使用表名:“update table1”。您在第二个示例中正确执行了此操作。
【解决方案3】:

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

任务:正确的信息,其中学生(即将离开中学的学生)在获得学校证书之前向大学提交了申请(是的,他们获得证书的时间早于颁发证书的时间(按指定的证书日期)。所以,我们将增加申请提交日期以适应证书颁发日期。

因此。下一个类似 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 自己的表(就像在子查询中一样)。

【讨论】:

  • 如何加入第三张桌子?
  • 你只要JOIN它像往常一样在FROM列表中:FROM abiturients b JOIN addresses c ON c.abiturient_id = b.id
  • @Envek - 唉,你不能在那里使用 JOIN,我刚刚检查过。 postgresql.org/docs/10/static/sql-update.html
  • @AdrianSmith,您不能在 UPDATE 本身中使用 JOIN,但可以在 UPDATE 的 from_list 子句(这是 PostgreSQL 对 SQL 的扩展)中使用它。此外,请参阅您提供的链接上有关加入表格注意事项的说明。
  • @Envek - 啊,谢谢你的澄清,我错过了。
【解决方案4】:

对于那些真正想做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”表。

【讨论】:

  • 考虑到这是第一个实际加入的答案(而不是在 with 子查询中),这应该是真正接受的答案。应该重命名这个问题或这个问题以避免混淆 postgresql 是否支持更新中的连接。
  • 需要注意的是,根据文档(postgresql.org/docs/11/sql-update.html),在from子句中列出目标表会导致目标表自连接。不太自信,在我看来,这是一个交叉自连接,可能会产生意想不到的结果和/或性能影响。
【解决方案5】:

对于那些想要进行仅更新您的联接返回的行的联接的人:

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;

上面已经提到了这一点,但只是通过评论..因为发布有效的新答案对于获得正确结果至关重要

【讨论】:

  • 正如@Ben Collins 提到的,这有意想不到的副作用postgresql.org/docs/11/sql-update.html
  • @FlipVernooij 在发布引用链接的评论时,请具体说明链接的引用部分和/或引用该部分,除非整个链接适用或链接的适用部分是非常明显。在这种情况下,您在引用的链接上所指的内容没有任何明显的意义,这让我们所有人都感到困惑,在浪费时间搜索链接的文档并返回问题“什么副作用??"
  • @FlipVernooij 加上答案的最后一行AND a.pk_id = a_alias.pk_id这里没有交叉连接,答案有效的。对 Ben 评论的链接和引用只会让读者白费力气,完全浪费时间,试图理解您所指的内容。
【解决方案6】:

我们开始吧:

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;

但是你有两次车辆表在那里,你只能给它起一个别名,你不能在“set”部分使用别名。

【讨论】:

  • @littlegreen 你确定吗? join 没有限制它吗?
  • @mpen 我可以确认它将所有记录更新为一个值。它没有达到您的预期。
【解决方案7】:

要为上述所有出色答案添加一些非常重要的内容,当您想要更新join-table 时,您可能会遇到两个问题:

  • 不能使用你想更新到JOIN另一个的表
  • Postgres 想要在 JOIN 之后有一个 ON 子句,所以你不能只使用 where 子句。

这意味着基本上,以下查询无效:

UPDATE join_a_b
SET count = 10
FROM a
JOIN b on b.id = join_a_b.b_id -- Not valid since join_a_b is used here
WHERE a.id = join_a_b.a_id
AND a.name = 'A'
AND b.name = 'B'
UPDATE join_a_b
SET count = 10
FROM a
JOIN b -- Not valid since there is no ON clause
WHERE a.id = join_a_b.a_id 
AND b.id = join_a_b.b_id
a.name = 'A'
AND b.name = 'B'

相反,您必须像这样使用FROM 子句中的所有表:

UPDATE join_a_b
SET count = 10
FROM a, b
WHERE a.id = join_a_b.a_id 
AND b.id = join_a_b.b_id 
AND a.name = 'A'
AND b.name = 'B'

这对某些人来说可能很简单,但我被困在这个问题上,想知道发生了什么,希望它能帮助其他人。

【讨论】:

    【解决方案8】:

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

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

    【讨论】:

      【解决方案9】:

      第一个表名: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';
      

      【讨论】:

        【解决方案10】:

        下面的链接有一个示例,可以解决并帮助更好地理解如何将 updatejoin 与 postgres 一起使用。

        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/

        【讨论】:

          猜你喜欢
          • 2016-03-15
          • 2013-06-20
          • 2014-01-15
          相关资源
          最近更新 更多