web-dev-qa-db-ja.com

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

これを行う簡単な方法は確かにありますが、適切な構文が見つかりません。では、どのようにしてこれをIn PostgreSQLに書くのでしょうか。

420
mpen

UPDATE構文 は次のとおりです。

 [WITH [RECURSIVE] with_query [、...]] UPDATE [ONLY]テーブル[[AS]エイリアス] 
 SET {column = {expression | DEFAULT} | 
(column [、...])=({expression | DEFAULT} [、...])} [、...] 
 [FROM from_list] 
 [WHERE条件| 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 
670
Mark Byers

私の例でもう少し説明しましょう。

課題:不正な情報(中学校を卒業しようとしている学生)が、学校の証明書を取得するよりも早く大学に申請書を提出した(はい、発行日よりも早く、証明書が発行されます)。証明書発行日に合わせて申請書の提出日を増やします。

したがって。次の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条件の1つになりました。これは他のANDと連関しています。そして、それ自身でJOINテーブルを作成する必要はもうありません(サブクエリのように)。

113
Envek

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 t1
set value = t.calculatedvalue
from t
where id = t.rowid

この方法では、selectクエリを開発してテストし、2つのステップでそれを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は列名のあいまいさについて不平を言います。

104
Alvin

実際に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"テーブルからのものと見なされるため、テーブル参照を必要としません。

48
Fast Engy

結合が返す行だけを更新する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;

これは上で言及されているが、コメントを通してのみ。

5
Nate Smith

さあ:

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"部分でエイリアスを使うことはできません。

4
mpen

以下のリンクは、postgresでupdatejoinをどのように使用するかを解決し、理解するのに役立つ例を示しています。

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

0
Alessandro

これは、NameのMiddle_Nameフィールドを使用してName3テーブルのMid_Nameを更新する単純なSQLです。

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