ON CONFLICT DO UPDATE
Davranışı netleştirin
Buradaki kılavuzu düşünün :
Ekleme için önerilen her bir satır için, ekleme işlemi devam eder veya belirtilen bir hakem kısıtlaması veya dizini conflict_target
ihlal edilirse
alternatif conflict_action
alınır.
Cesur vurgu benim. Dolayısıyla WHERE
, UPDATE
(the conflict_action
) maddesindeki benzersiz dizine dahil edilen sütunlar için tahminleri tekrarlamanız gerekmez :
INSERT INTO test_upsert AS tu
(name , status, test_field , identifier, count)
VALUES ('shaun', 1 , 'test value', 'ident' , 1)
ON CONFLICT (name, status, test_field) DO UPDATE
SET count = tu.count + 1;
WHERE tu.name = 'shaun' AND tu.status = 1 AND tu.test_field = 'test value'
Eşsiz ihlal, ek WHERE
maddenizin gereğinden fazla zorlayacağını zaten tespit ediyor .
Kısmi dizini açıklığa kavuştur
Kendinizden bahsettiğiniz gibi WHERE
gerçek bir kısmi dizin yapmak için bir cümle ekleyin (ancak ters mantıkla):
CREATE UNIQUE INDEX test_upsert_partial_idx
ON public.test_upsert (name, status)
WHERE test_field IS NULL; -- not: "is not null"
To kullanmak için Upsert bu kısmi endeksi bir eşleştirme ihtiyaç @ypercube gösterir gibi :conflict_target
ON CONFLICT (name, status) WHERE test_field IS NULL
Şimdi yukarıdaki kısmi endeks çıkarılmaktadır. Ancak , el kitabında da belirtildiği gibi :
[...] kısmi olmayan benzersiz bir indeks (yüklemi olmayan benzersiz bir indeks), ON CONFLICT
diğer tüm kriterleri karşılayan böyle bir indeks mevcut olduğunda çıkartılacaktır (ve böylece tarafından kullanılacaktır ).
Ek (veya yalnızca) bir dizininiz varsa, yalnızca (name, status)
(ayrıca) kullanılır. Üzerinde bir dizin (name, status, test_field)
açıkça olurdu değil çıkarılabilir. Bu sorununuzu açıklamaz, ancak test sırasında karışıklığa neden olmuş olabilir.
Çözüm
AIUI, yukarıdakilerin hiçbiri sorununuzu henüz çözmedi . Kısmi endeks ile yalnızca eşleşen NULL değerleri olan özel durumlar yakalanır. Eşleşen başka benzersiz dizinler / kısıtlamalarınız yoksa diğer yinelenen satırlar eklenir veya varsa bir istisna oluşturur. Sanırım istediğin bu değil. Sen yaz:
Kompozit anahtar, 10'u boş olabilen 20 sütundan oluşur.
Tam olarak neyi yineleniyorsunuz? Postgres (SQL standardına göre) iki NULL değerinin eşit olduğunu düşünmez. Kullanım kılavuzu:
Genel olarak, tabloda, kısıtlamaya dahil edilen tüm sütunların değerlerinin eşit olduğu birden fazla satır varsa benzersiz bir kısıtlama ihlal edilir. Ancak bu karşılaştırmada iki boş değer asla eşit kabul edilmez. Bu, benzersiz bir kısıtlamanın varlığında bile, kısıtlanmış sütunlardan en az birinde boş değer içeren yinelenen satırların saklanabileceği anlamına gelir. Bu davranış, SQL standardına uygundur, ancak diğer SQL veritabanlarının bu kurala uymayabileceğini duyduk. Bu nedenle taşınabilir olması amaçlanan uygulamalar geliştirirken dikkatli olun.
İlişkili:
NULL
Her 10 null sütunundaki değerlerin eşit kabul edilmesiniistediğinizi varsayalım . Burada gösterildiği gibi ek kısmi bir dizine sahip tek bir boş sütunun kapsanması zarif ve pratiktir:
Ancak, daha boş sütunlar için bu hızla elden çıkar. Sıfırlanabilir sütunların her farklı birleşimi için kısmi bir dizine ihtiyacınız vardır. Sadece 2 için 3 kısmi endeksler var olanların için (a)
, (b)
ve (a,b)
. Sayı katlanarak artıyor 2^n - 1
. 10 boş değerli sütununuz için, olası tüm NULL değer kombinasyonlarını kapsamak için zaten 1023 kısmi dizine ihtiyacınız vardır. Gitme.
Basit çözüm: NULL değerleri değiştirin ve ilgili sütunları tanımlayın NOT NULL
, her şey basit bir UNIQUE
kısıtlamayla sorunsuz çalışır .
Bu bir seçenek değilse COALESCE
, dizinde NULL yerine bir ifade dizini öneririz :
CREATE UNIQUE INDEX test_upsert_solution_idx
ON test_upsert (name, status, COALESCE(test_field, ''));
Boş dize ( ''
) karakteri türleri için bariz bir aday olduğunu, ancak kullanabileceğiniz herhangi görünür ya göre NULL ile katlanabilir ya hiç yasal değeri sizin "benzersiz" tanımına.
Sonra şu ifadeyi kullanın:
INSERT INTO test_upsert as tu(name,status,test_field,identifier, count)
VALUES ('shaun', 1, null , 'ident', 11) -- works with
, ('bob' , 2, 'test value', 'ident', 22) -- and without NULL
ON CONFLICT (name, status, COALESCE(test_field, '')) DO UPDATE -- match expr. index
SET count = COALESCE(tu.count + EXCLUDED.count, EXCLUDED.count, tu.count);
@Ypercube gibi count
, mevcut sayıma eklemek istediğinizi varsayalım . Sütun NULL olabileceğinden, NULL eklenmesi NULL sütununu ayarlar. Tanımlarsanız count NOT NULL
, basitleştirebilirsiniz.
Başka bir fikir sadece düşmesi olacaktır conflict_target kapsayacak şekilde ifadeden tüm benzersiz ihlalleri . Ardından, "benzersiz" olması gerekenlerin daha karmaşık bir tanımı için çeşitli benzersiz dizinler tanımlayabilirsiniz. Ama bu uçmayacak ON CONFLICT DO UPDATE
. Kılavuz bir kez daha:
Için ON CONFLICT DO NOTHING
, bir anlaşmazlık_hedef belirtmek isteğe bağlıdır; atlandığında, tüm kullanılabilir kısıtlamalarla (ve benzersiz dizinlerle) çakışmalar ele alınır. İçin ON CONFLICT DO UPDATE
, bir conflict_target gerekir sağlanacaktır.
count = CASE WHEN EXCLUDED.count IS NULL THEN tu.count ELSE COALESCE(tu.count, 0) + COALESCE(EXCLUDED.count, 0) END
Basitleştiirlebilircount = COALESCE(tu.count+EXCLUDED.count, EXCLUDED.count, tu.count)