PostgreSQLで更新と結合を行う方法は?


508

基本的に、私はこれをしたいです:

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で書くのですか?


5
Postgresの構文は異なります:postgresql.org/docs/8.1/static/sql-update.html
Marc B

4
Vehicles_vehicle、shippings_shipment?これは興味深いテーブルの命名規則です
CodeAndCats 2017年

3
@CodeAndCatsははは...おかしく見えませんか?私は当時Djangoを使用していたと思います。テーブルは機能ごとにグループ化されています。したがって、ビューvehicles_*テーブルといくつかのshipments_*テーブルがあったでしょう。
mpen 2017年

回答:


776

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

[WITH [RECURSIVE] with_query [、...]]
UPDATE [ONLY] table [[AS] alias]
    SET {列= {式| デフォルト} |
          (column [、...])=({expression | DEFAULT} [、...])} [、...]
    [FROM from_list]
    [WHERE条件| カーソルの現在の場所]
    [戻る* | 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

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条件の1つになり、他の条件と結合しANDて、変更せずにサブクエリから移動されています。そしてJOIN、それ自体でサブテーブルを作成する必要はありません(サブクエリのように)。


16
どのようにして3番目のテーブルに参加しますか?
グロウラー2017

19
あなたはJOINそれをいつものようにFROMリストに載せます:FROM abiturients b JOIN addresses c ON c.abiturient_id = b.id
Envek

@Envek-ああ、そこにJOINを使用することはできません。postgresql.org/docs/10/static/sql-update.html
Adrian Smith

3
@ AdrianSmith、UPDATE自体ではJOINを使用できませんが、UPDATEのfrom_list句(PostgreSQLのSQLの拡張)で使用できます。また、提供したリンクにあるテーブルの結合に関する注意事項に関する注意も参照してください。
Envek 2017年

@Envek-ああ、説明をありがとう、私はそれを逃した。
エイドリアン・スミス

130

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

このアプローチでは、選択クエリを開発してテストし、2つのステップでそれを更新クエリに変換できます。

したがって、あなたの場合、結果のクエリは次のようになります。

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バージョンは、上記の「単純な結合」ソリューションよりもはるかに高速です
paul.ago

このソリューションの他の利点は、with / selectステートメントで複数の結合を使用することにより、3つ以上のテーブルから結合して最終的な計算値に到達できることです。
Alex Muro

1
これは素晴らしいです。私は厳選された細工を施し、@ dannysauerのように、改宗を恐れていました。これは単に私のためにそれを行います。パーフェクト!
凍りつくような素晴らしい

1
最初のSQLの例に構文エラーがあります。"update t1"はtサブクエリのエイリアスを使用できません。テーブル名を使用する必要があります: "update table1"。2番目の例では、これを正しく行います。
EricS

80

実際にやりたい人のために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
これが実際の結合(およびwithサブクエリ内ではない)の最初の回答であることを考えると、これは実際に受け入れられる回答になるはずです。それか、この質問の名前を変更して、postgresqlが更新での結合をサポートするかどうかの混乱を回避する必要があります。
ネックレス

できます。しかし、ハックのように感じています
M.ハビブ

ドキュメント(postgresql.org/docs/11/sql-update.html)によると、ターゲットテーブルをfrom句にリストすると、ターゲットテーブルが自己結合されることに注意してください。自信がありませんが、これはクロスセルフジョインであるようにも見えます。これは、意図しない結果やパフォーマンスに影響を与える可能性があります。
ベンコリンズ

14

ジョインが返す行のみを更新する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;

ただし、vehicleテーブルが2回あり、エイリアスを設定できるのは1回だけであり、「セット」部分でエイリアスを使用することはできません。


@littlegreenよろしいですか?join制約しませんか?
mpen

4
@mpenすべてのレコードが1つの値に更新されることを確認できます。それはあなたが期待することをしません。
Adam Bell

1

次に、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

最初のテーブル名:tbl_table1(tab1)。2番目のテーブル名: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';
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.