Bu bir uygulama kararıdır. Postgres belgelerinde WITH
Sorgular (Ortak Tablo İfadeleri) bölümünde açıklanmıştır . Konuyla ilgili iki paragraf var.
İlk olarak, gözlemlenen davranışın nedeni:
İçindeki alt deyimlerWITH
birbirleriyle ve ana sorgu ile eşzamanlı olarak yürütülür . Bu nedenle, içinde veri değiştiren ifadeler kullanıldığında WITH
, belirtilen güncellemelerin gerçekleşme sırası tahmin edilemez. Tüm ifadeler aynı anlık görüntü ile yürütülür (bakınız Bölüm 13), böylece birbirlerinin hedef tablolar üzerindeki etkilerini "göremezler". Bu, satır güncellemelerinin gerçek sırasının öngörülemezliğinin etkilerini hafifletir ve verilerin, farklı alt ifadeler ile ana sorgu arasındaki değişiklikleri iletmenin tek yolu olduğu anlamına gelir . RETURNING
WITH
Bunun bir örneği ...
Ben pgsql-docs için bir öneri gönderdikten sonra , Marko Tiikkaja açıkladı (Erwin'in cevabına katılıyor):
Insert-update ve insert-delete durumları, UPDATE'lerin ve DELETE'lerin, INSERT gerçekleşmeden önce anlık görüntüsü alınmış olmaları nedeniyle INSERTed satırlarını görmenin bir yolu olmadığından çalışmaz. Bu iki vaka hakkında öngörülemez bir şey yok.
Dolayısıyla, ifadenizin güncellenmemesinin nedeni yukarıdaki ilk paragrafla açıklanabilir (yaklaşık "anlık görüntüler"). CTE'leri değiştirdiğinizde ne olacağı, hepsinin ve ana sorgunun yürütülmesi ve ifadenin yürütülmesinden hemen önceki verilerle (tabloların) aynı anlık görüntüsünü "görmesi" dir. CTE'ler, RETURNING
maddeyi kullanarak birbirlerine ve ana sorguya ekledikleri / güncelledikleri / sildikleri hakkında bilgi aktarabilirler, ancak tablolardaki değişiklikleri doğrudan göremezler. Şimdi ifadenizde neler olduğunu görelim:
WITH newval AS (
INSERT INTO tbl(val) VALUES (1) RETURNING id
) UPDATE tbl SET val=2 FROM newval WHERE tbl.id=newval.id;
CTE ( newval
) olmak üzere 2 bölümümüz var :
-- newval
INSERT INTO tbl(val) VALUES (1) RETURNING id
ve ana sorgu:
-- main
UPDATE tbl SET val=2 FROM newval WHERE tbl.id=newval.id
İcra akışı şu şekildedir:
initial data: tbl
id │ val
(empty)
/ \
/ \
/ \
newval: \
tbl (after newval) \
id │ val \
1 │ 1 |
|
newval: returns |
id |
1 |
\ |
\ |
\ |
main query
Sonuç olarak, ana sorgu tbl
(anlık görüntüde görüldüğü gibi) newval
tabloyla birleştiğinde, boş bir tabloyu 1 satırlık bir tabloyla birleştirir. Açıkçası 0 satırı güncelliyor. Yani ifade, yeni eklenen satırı değiştirmek için hiç gelmedi ve gördüğünüz şey bu.
Sizin durumunuzdaki çözüm, ya ilk etapta doğru değerleri eklemek için ifadeyi yeniden yazmak ya da 2 ifadeyi kullanmaktır. Biri ekler ve diğeri güncellenir.
İfadenin aynı satırlarda bir INSERT
ve sonra a olması gibi başka benzer durumlar da vardır DELETE
. Silme işlemi aynı nedenlerle başarısız olur.
Güncelleme-güncelleme ve güncelleme-silme ile bazı diğer durumlar ve bunların davranışları, aşağıdaki dokümanda aynı dokümanlar sayfasında açıklanmaktadır.
Aynı satırı tek bir deyimde iki kez güncellemeye çalışmak desteklenmez. Değişikliklerden sadece biri gerçekleşir, ancak hangisini güvenilir bir şekilde tahmin etmek kolay değildir (ve bazen mümkün değildir). Bu, aynı ifadede önceden güncellenmiş bir satırı silmek için de geçerlidir: sadece güncelleme gerçekleştirilir. Bu nedenle, genellikle tek bir ifadede tek bir satırı iki kez değiştirmeye çalışmaktan kaçınmalısınız. Özellikle, ana ifade veya kardeş alt deyimi tarafından değiştirilen aynı satırları etkileyebilecek WITH alt deyimleri yazmaktan kaçının. Böyle bir ifadenin etkileri öngörülemez.
Marko Tiikkaja'nın cevabında:
Güncelleme güncelleme ve güncelleme-silme vakalar açıkça edilir değil (insert-update ve insert-silme vakalar gibi) aynı temel uygulama detay neden.
Güncelleme güncelleme vakası işe yaramaz çünkü dahili olarak Cadılar Bayramı problemine benziyor ve Postgres, hangi tuples'ların iki kez güncellenmenin uygun olacağını ve hangilerinin Cadılar Bayramı problemini yeniden başlatabileceğini bilmenin bir yolu yok.
Bu nedenle neden aynıdır (CTE'lerin nasıl değiştirildiği ve her CTE'nin aynı anlık görüntüyü nasıl gördüğü), ancak bu 2 durumda ayrıntılar daha karmaşık olduğundan ve sonuçlar güncelleme güncelleme durumunda tahmin edilemediğinden farklıdır.
Insert-update (sizin durumunuz gibi) ve benzer bir insert-delete'de sonuçlar tahmin edilebilir. İkinci işlemin (güncelleme veya silme) yeni eklenen satırları görmesi ve etkilemesi mümkün olmadığından yalnızca ekleme yapılır.
Önerilen çözüm, aynı satırları bir kereden fazla değiştirmeye çalışan tüm durumlar için aynıdır: Yapmayın. Her satırı bir kez değiştiren veya ayrı (2 veya daha fazla) deyim kullanan deyimler yazın.