PostgreSQL'de update + join nasıl yapılır?


508

Temel olarak, bunu yapmak istiyorum:

update vehicles_vehicle v 
    join shipments_shipment s on v.shipment_id=s.id 
set v.price=s.price_per_vehicle;

Bunun MySQL'de (arka planım) çalışacağından eminim, ancak postgres'te çalışmıyor gibi görünüyor. Aldığım hata:

ERROR:  syntax error at or near "join"
LINE 1: update vehicles_vehicle v join shipments_shipment s on v.shi...
                                  ^

Elbette bunu yapmanın kolay bir yolu var, ancak doğru sözdizimini bulamıyorum. Peki, bunu PostgreSQL'e nasıl yazarım?


5
Postgres sözdizimi farklıdır: postgresql.org/docs/8.1/static/sql-update.html
Marc B

4
Vehicles_vehicle, gönderiler_nakliye? Bu ilginç bir tablo adlandırma kuralı
CodeAndCats

3
@CodeAndCats Haha ... komik görünüyor değil mi? Sanırım o sırada Django kullanıyordum ve tablolar özelliğe göre gruplandırılmış. Yani bir görünüm vehicles_*tabloları ve birkaç shipments_*tablo olurdu .
17'de mpen

Yanıtlar:


776

GÜNCELLEME sözdizimi geçerli:

[İLE [RECURSIVE] with_query [, ...]]
GÜNCELLEME [YALNIZCA] tablosu [[AS] takma adı]
    SET {column = {ifade | VARSAYILAN} |
          (sütun [, ...]) = ({ifade | DEFAULT} [, ...])} [, ...]
    [FROM from_list]
    [NEREDE koşul | İmleç_adı NEREDE MEVCUT]
    [İADE * | çıktı_ifadesi [[AS] çıktı_adı] [, ...]]

Sizin durumunuzda bunu istediğinizi düşünüyorum:

UPDATE vehicles_vehicle AS v 
SET price = s.price_per_vehicle
FROM shipments_shipment AS s
WHERE v.shipment_id = s.id 

Güncelleştirme tüm tablo birleşimleri listesine dayanıyorsa, bunlar GÜNCELLEME bölümünde mi yoksa FROM bölümünde mi olmalıdır?
ted.strauss

11
@ ted.strauss: FROM bir tablo listesi içerebilir.
Mark Byers

140

Örnekle biraz daha açıklayayım.

Görev: Abiturientlerin (ortaokuldan ayrılmak üzere olan öğrenciler) okul sertifikaları aldıklarından daha önce üniversiteye başvuru yaptıkları doğru bilgi (evet, verildiklerinden daha önce sertifika almışlardır (belirtilen sertifika tarihine göre). Sertifika verme tarihine uyacak şekilde başvuru gönderme tarihini artırın.

Böylece. sonraki MySQL benzeri ifade:

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 benzeri olur

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

Gördüğünüz gibi, orijinal alt sorgu JOIN'ın ONhükmü biri haline gelmiştir WHEREtarafından conjucted olduğu koşullar, ANDhiçbir değişiklik olmadan alt sorgu taşındığını başkalarıyla birlikte. Ve JOIN(alt sorguda olduğu gibi) kendisi ile tablolamaya artık gerek yok .


16
Üçüncü bir masaya nasıl katılırsınız?
Growler

19
Sadece JOINo her zamanki gibi FROMlistenin:FROM abiturients b JOIN addresses c ON c.abiturient_id = b.id
Envek

@Envek - Ne yazık ki orada JOIN kullanamazsınız, sadece kontrol ettim. postgresql.org/docs/10/static/sql-update.html
Adrian Smith

3
@AdrianSmith, JOIN'i UPDATE içinde kullanamazsınız, ancak UPDATE from_listdeyiminde (PostgreSQL'in SQL uzantısıdır) kullanabilirsiniz. Ayrıca, sağladığınız bağlantıda tablo uyarılarına katılma ile ilgili notlara bakın.
Envek

@Envek - Ah, açıklama için teşekkürler, kaçırdım.
Adrian Smith

130

Mark Byers'ın cevabı bu durumda en uygunudur. Daha karmaşık durumlarda, rowids ve hesaplanan değerleri döndüren seçme sorgusunu alıp güncelleme sorgusuna şu şekilde ekleyebilirsiniz:

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

Bu yaklaşım, seçili sorgunuzu geliştirmenize ve test etmenize olanak tanır ve iki adımda bu sorguyu güncelleme sorgusuna dönüştürür.

Yani sizin durumunuzda sonuç sorgusu:

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

Sütun takma adlarının zorunlu olduğunu unutmayın, aksi takdirde PostgreSQL sütun adlarının belirsizliğinden şikayetçi olacaktır.


1
Bunu çok beğendim çünkü her zaman biraz benim üstümden "select" alarak ve özellikle birden fazla birleştirme ile bir "güncelleme" ile yerine biraz gerginim. Bu, toplu güncellemelerden önce yapmam gereken SQL döküm sayısını azaltır. :)
dannysauer

5
Emin değilim, ancak bu sorgunun CTE sürümü yukarıdaki "düz birleştirme" çözümlerinden çok daha hızlı
paul.ago

Bu çözümün diğer avantajı, with / select deyiminde birden çok birleşim kullanarak hesaplanan son değerinize ulaşmak için ikiden fazla tablodan birleştirme yeteneğidir.
Alex Muro

1
Bu harika. Seçimimi hazırladım ve @dannysauer gibi, dönüşümden korktum. Bu sadece benim için yapar. Mükemmel!
Ayaz

1
İlk SQL örneğinizde bir sözdizimi hatası var. "t1 güncellemesi" t alt sorgusundaki diğer adı kullanamaz, tablo adını kullanması gerekir: "update table1". Bunu ikinci örneğinizde doğru şekilde yaparsınız.
EricS

80

Aslında yapmak isteyenler için JOINşunları da kullanabilirsiniz:

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;

SETGerekirse eşittir işaretinin sağındaki bölümde a_alias kullanabilirsiniz . Eşittir işaretinin solundaki alanlar, orijinal "a" tablosundan oldukları kabul edilen bir tablo başvurusu gerektirmez.


4
Bunun gerçek bir birleştirmeyle (ve alt sorgu ile a içinde değil) ilk cevap olduğu düşünüldüğünde, bu kabul edilen gerçek cevap olmalıdır. Postgresql'in güncellemedeki birleştirmeleri destekleyip desteklememesi konusundaki karışıklığı önlemek için bu soru veya bu soru yeniden adlandırılmalıdır.
kolye

İşe yarıyor. Ama bir hack gibi hissediyor
M. Habib

Belgelere göre ( postgresql.org/docs/11/sql-update.html ), from yan tümcesinde hedef tabloyu listelemenin hedef tablonun kendiliğinden birleşmesine neden olacağı unutulmamalıdır. Daha az güvenle, bana bunun, istenmeyen sonuçlara ve / veya performans sonuçlarına sahip olabilen çapraz kendi kendine birleşme olduğu da anlaşılıyor.
Ben Collins

14

SADECE birleştirmenizin döndürdüğü satırları güncelleyen bir JOIN yapmak isteyenler için:

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;

Bu yukarıda bahsedildi, ancak sadece bir yorum yoluyla .. Doğru sonuç almak için kritik önem taşımaktadır.


7

İşte başlıyoruz:

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

Yapabildiğim kadar basit. Teşekkürler beyler!

Bunu da yapabilir:

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

Ama sonra araç tablosunu iki kez içeri aldınız ve sadece bir kez takma adınıza izin verdiniz ve "takma" bölümünde takma adı kullanamazsınız.


@littlegreen Bundan emin misin? Does not joinit sınırlamak?
mpen

4
@mpen Tüm kayıtları tek bir değere güncellediğini onaylayabilirim. beklediğiniz şeyi yapmaz.
Adam Bell

1

Name3'teki Middle_Name alanını kullanarak Name3 tablosundaki Mid_Name'i güncelleştiren basit bir SQL:

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


0

İlk Tablo Adı: tbl_table1 (tab1). İkinci Tablo Adı: tbl_table2 (tab2).

Tbl_table1'in ac_status sütununu "INACTIVE" olarak ayarlayın

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';
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.