Örnek bir tablo ve veriler
CREATE TABLE dupes(col1 int primary key, col2 int, col3 text,
CONSTRAINT col2_unique UNIQUE (col2)
);
INSERT INTO dupes values(1,1,'a'),(2,2,'b');
Sorunu yeniden üretmek
INSERT INTO dupes values(3,2,'c')
ON CONFLICT (col1) DO UPDATE SET col3 = 'c', col2 = 2
Buna Q1 diyelim. Sonuç
ERROR: duplicate key value violates unique constraint "col2_unique"
DETAIL: Key (col2)=(2) already exists.
çatışma_target benzersiz dizin çıkarımı gerçekleştirebilir. Çıkarım gerçekleştirirken, bir veya daha fazla index_column_name sütunundan ve / veya index_expression ifadesinden ve isteğe bağlı bir index_predicate'den oluşur. Sıraya bakılmaksızın, tam olarak çakışan hedefle belirtilen sütunları / ifadeleri içeren tüm tablo_adı benzersiz dizinler, hakem dizinleri olarak çıkarılır (seçilir). Bir index_predicate belirtilmişse, çıkarım için ek bir gereklilik olarak hakem indekslerini karşılaması gerekir.
Bu, aşağıdaki sorgunun çalışması gerektiği izlenimini verir, ancak aslında col1 ve col2'de birlikte benzersiz bir dizin gerektireceği için değildir. Bununla birlikte, böyle bir endeks, OP'nin gereksinimlerinden biri olan col1 ve col2'nin ayrı ayrı benzersiz olacağını garanti etmez.
INSERT INTO dupes values(3,2,'c')
ON CONFLICT (col1,col2) DO UPDATE SET col3 = 'c', col2 = 2
Bu sorguyu Q2 olarak adlandıralım (bu bir sözdizimi hatasıyla başarısız olur)
Neden?
Postgresql bu şekilde davranır çünkü ikinci sütunda bir çakışma olduğunda ne olması gerektiği iyi tanımlanmamıştır. Bir dizi olasılık var. Örneğin, yukarıdaki Q1 sorgusunda, col1
üzerinde bir çakışma olduğunda postgresql güncellenmeli col2
mi? Ama ya bu başka bir çatışmaya yol açarsa col1
? postgresql'in bunu nasıl ele alması bekleniyor?
Bir çözüm
Bir çözüm, ON CONFLICT'i eski moda UPSERT ile birleştirmektir .
CREATE OR REPLACE FUNCTION merge_db(key1 INT, key2 INT, data TEXT) RETURNS VOID AS
$$
BEGIN
LOOP
UPDATE dupes SET col3 = data WHERE col1 = key1 and col2 = key2;
IF found THEN
RETURN;
END IF;
BEGIN
INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col1) DO UPDATE SET col3 = data;
RETURN;
EXCEPTION WHEN unique_violation THEN
BEGIN
INSERT INTO dupes VALUES (key1, key2, data) ON CONFLICT (col2) DO UPDATE SET col3 = data;
RETURN;
EXCEPTION WHEN unique_violation THEN
END;
END;
END LOOP;
END;
$$
LANGUAGE plpgsql;
Sütunları tam olarak istediğiniz şekilde güncellemesi için bu depolanan işlevin mantığını değiştirmeniz gerekir. Gibi çağır
SELECT merge_db(3,2,'c');
SELECT merge_db(1,2,'d');