9.5 ve daha yenisi:
PostgreSQL 9.5 ve daha yeni destek INSERT ... ON CONFLICT UPDATE
(ve ON CONFLICT DO NOTHING
), yani upert.
İle karşılaştırmaON DUPLICATE KEY UPDATE
.
Hızlı açıklama .
Kullanım için el kitabına bakın - özellikle sözdizimi diyagramındaki çakışma_etlemi yan tümcesine ve açıklayıcı metne .
Aşağıda verilen 9.4 ve daha eski çözümlerden farklı olarak, bu özellik birden çok çakışan satırla çalışır ve özel kilitleme veya yeniden deneme döngüsü gerektirmez.
Özelliği ekleme taahhüdü burada ve geliştirilmesiyle ilgili tartışma burada .
9.5 kullanıyorsanız ve geriye dönük uyumlu olmanız gerekmiyorsa, şimdi okumayı durdurabilirsiniz .
9.4 ve üstü:
PostgreSQL'in yerleşik UPSERT
(veya MERGE
) bir tesisi yoktur ve eşzamanlı kullanım karşısında verimli bir şekilde yapmak çok zordur.
Bu makalede, sorunu ayrıntılı olarak açıklanmaktadır .
Genel olarak iki seçenek arasında seçim yapmanız gerekir:
- Yeniden deneme döngüsünde tek tek ekleme / güncelleme işlemleri; veya
- Masayı kilitleme ve toplu birleştirme yapıyor
Tek satır yeniden deneme döngüsü
Birden çok bağlantıyı aynı anda ekler yapmaya çalışırken istiyorsanız, yeniden deneme döngüsünde tek tek satır üstbilgileri kullanmak makul bir seçenektir.
PostgreSQL belgeleri, bunu veritabanının içindeki bir döngüde yapmanıza izin veren kullanışlı bir yordam içerir . Çoğu naif çözümün aksine, kayıp güncellemelere karşı koruma sağlar ve yarışları yerleştirir. Sadece READ COMMITTED
modda çalışır ve ancak işlemde yaptığınız tek şey güvenli olduğunda güvenlidir. Tetikleyiciler veya ikincil benzersiz anahtarlar benzersiz ihlallere neden olursa işlev düzgün çalışmaz.
Bu strateji çok verimsiz. Ne zaman pratik olursanız olun, işi sıraya almalı ve bunun yerine aşağıda açıklandığı gibi bir toplu destek yapmalısınız.
Bu soruna yönelik birçok girişim çözümü geri alma işlemlerini dikkate almaz, bu nedenle eksik güncellemelerle sonuçlanır. İki işlem birbiriyle yarışır; bunlardan biri başarıyla INSERT
s; diğeri yinelenen bir anahtar hatası alır ve UPDATE
bunun yerine bir anahtar yapar . UPDATE
İçin bloklar bekleyen INSERT
geri alma veya taahhüt. Geri döndüğünde, UPDATE
koşulun yeniden kontrolü sıfır satırla eşleşir, bu nedenle UPDATE
taahhütler gerçekte beklediğiniz yükselmeyi yapmamış olsa bile . Sonuç satırı sayılarını kontrol etmeniz ve gerektiğinde yeniden denemeniz gerekir.
Bazı denenmiş çözümler SELECT ırklarını dikkate almaz. Açık ve basit olanı denerseniz:
-- THIS IS WRONG. DO NOT COPY IT. It's an EXAMPLE.
BEGIN;
UPDATE testtable
SET somedata = 'blah'
WHERE id = 2;
-- Remember, this is WRONG. Do NOT COPY IT.
INSERT INTO testtable (id, somedata)
SELECT 2, 'blah'
WHERE NOT EXISTS (SELECT 1 FROM testtable WHERE testtable.id = 2);
COMMIT;
daha sonra iki seferde birden fazla hata modu olduğunda. Bunlardan biri, güncellemenin yeniden kontrol edilmesinde zaten tartışılan konudur. Bir diğeri, her ikisinin UPDATE
de aynı anda sıfır satırı eşleştirmesi ve devam etmesidir. Sonra ikisini de EXISTS
olur testini önceINSERT
. Her ikisi de sıfır satır alır, her ikisi de INSERT
. Biri yinelenen bir anahtar hatasıyla başarısız oluyor.
Bu yüzden bir yeniden deneme döngüsüne ihtiyacınız var. Akıllı SQL ile yinelenen anahtar hatalarını veya kayıp güncellemeleri önleyebileceğinizi düşünebilirsiniz, ancak yapamazsınız. Satır sayısını kontrol etmeniz veya yinelenen anahtar hatalarını (seçilen yaklaşıma bağlı olarak) ele almanız ve tekrar denemeniz gerekir.
Lütfen bunun için kendi çözümünüzü atmayın. Mesaj kuyruğunda olduğu gibi, muhtemelen yanlış.
Kilitli toplu upsert
Bazen, daha eski bir veri kümesiyle birleştirmek istediğiniz yeni bir veri kümesine sahip olduğunuz toplu upert yapmak istersiniz. Bu, bireysel sıra destekçilerinden çok daha verimlidir ve pratik olduğunda tercih edilmelidir.
Bu durumda, genellikle aşağıdaki işlemi izlersiniz:
CREATE
bir TEMPORARY
masa
COPY
veya yeni verileri geçici tabloya toplu olarak ekleyin
LOCK
hedef tablo IN EXCLUSIVE MODE
. Bu, başka işlemlere izin verir SELECT
, ancak tabloda değişiklik yapmaz.
UPDATE ... FROM
Geçici tablodaki değerleri kullanarak varolan kayıtlardan birini yapın ;
INSERT
Hedef tabloda henüz bulunmayan satırlardan birini yapın ;
COMMIT
, kilidi serbest bırakır.
Örneğin, soruda verilen örnek için INSERT
, geçici tabloyu doldurmak için çok değerli kullanma:
BEGIN;
CREATE TEMPORARY TABLE newvals(id integer, somedata text);
INSERT INTO newvals(id, somedata) VALUES (2, 'Joe'), (3, 'Alan');
LOCK TABLE testtable IN EXCLUSIVE MODE;
UPDATE testtable
SET somedata = newvals.somedata
FROM newvals
WHERE newvals.id = testtable.id;
INSERT INTO testtable
SELECT newvals.id, newvals.somedata
FROM newvals
LEFT OUTER JOIN testtable ON (testtable.id = newvals.id)
WHERE testtable.id IS NULL;
COMMIT;
İlgili okuma
Ne olmuş MERGE
?
SQL standardı MERGE
aslında kötü tanımlanmış eşzamanlılık semantiğine sahiptir ve önce bir tabloyu kilitlemeden yükseltme için uygun değildir.
Veri birleştirme için gerçekten yararlı bir OLAP ifadesi, ancak aslında eşzamanlı güvenli destek için yararlı bir çözüm değil. Diğer DBMS'leri kullanıcıları için kullanmak MERGE
üzere birçok öneri var, ama aslında yanlış.
Diğer DB'ler: