Düz INSERT
INSERT INTO bar (description, foo_id)
SELECT val.description, f.id
FROM (
VALUES
(text 'testing', text 'blue') -- explicit type declaration; see below
, ('another row', 'red' )
, ('new row1' , 'purple') -- purple does not exist in foo, yet
, ('new row2' , 'purple')
) val (description, type)
LEFT JOIN foo f USING (type);
Bir kullanımı LEFT [OUTER] JOIN
yerine [INNER] JOIN
gelen satırları demektir val
düştü değildir eşleşme bulunduğunda foo
. Bunun yerine, NULL
için girilir foo_id
.
Alt VALUES
sorgudaki ifade, @ ypercube's CTE ile aynıdır . Genel Tablo İfadeleri ek özellikler sunar ve büyük sorgularda okunması daha kolaydır, ancak bunlar aynı zamanda optimizasyon engelleri oluşturur. Bu nedenle, alt sorguların hiçbiri, yukarıdakilerin hiçbirine ihtiyaç duyulmadığında biraz daha hızlıdır.
id
Sütun adı geniş yayılımlı bir kalıptır. Olumlu foo_id
ve bar_id
açıklayıcı bir şey olmalı . Bir grup masaya katılırken, hepsinin adında birden fazla sütun bulunur id
.
Düz text
veya varchar
yerine düşünün varchar(n)
. Bir uzunluk kısıtlaması gerçekten uygulamanız gerekiyorsa, bir CHECK
sınırlama ekleyin :
Açık tür yayınlar eklemeniz gerekebilir. İtibaren VALUES
sentezleme ile doğrudan (olduğu gibi bir tablo bağlı değildir INSERT ... VALUES ...
), çeşitleri elde edilemez ve varsayılan veri türleri her durumda çalışmayabilir olan, açık tür bildirimi olmaksızın kullanılır. İlk satırda yapmak yeterli, gerisi sırayla düşecek.
INSERT eksik FK satırları aynı anda
foo
Anında varolmayan girişler oluşturmak istiyorsanız , tek bir SQL ifadesinde , CTE'ler araçsaldır:
WITH sel AS (
SELECT val.description, val.type, f.id AS foo_id
FROM (
VALUES
(text 'testing', text 'blue')
, ('another row', 'red' )
, ('new row1' , 'purple')
, ('new row2' , 'purple')
) val (description, type)
LEFT JOIN foo f USING (type)
)
, ins AS (
INSERT INTO foo (type)
SELECT DISTINCT type FROM sel WHERE foo_id IS NULL
RETURNING id AS foo_id, type
)
INSERT INTO bar (description, foo_id)
SELECT sel.description, COALESCE(sel.foo_id, ins.foo_id)
FROM sel
LEFT JOIN ins USING (type);
Takılacak iki yeni boş satıra dikkat edin. Her ikisi de henüz var olmayan mor renktedirfoo
. İlk açıklamada gerekliliği göstermek için iki satır .DISTINCT
INSERT
Adım adım açıklama
1. CTE sel
katları girdi verisi satırları sağlar. İfadeli alt sorgu val
, VALUES
bir tablo veya kaynak olarak alt sorgu ile değiştirilebilir. Hemen LEFT JOIN
etmek foo
eklenecek foo_id
için önceden varolan type
satırları. Diğer tüm satırlar foo_id IS NULL
bu şekilde olsun .
2. CTE farklı yeni tipler ins
ekler ( ) içine yerleştirir ve yeni üretilenleri döndürür - birlikte satırları eklemek için geri bir araya gelirler .foo_id IS NULL
foo
foo_id
type
Son dış INSERT
şimdi her satır için bir foo.id ekleyebilir: önceden var olan tür veya 2. adımda eklenmiş.
Açıkçası, her iki uç da "paralel" olur, ancak bu tek bir ifade olduğu için varsayılan FOREIGN KEY
kısıtlamalar şikayet etmeyecektir. Referans bütünlüğü, varsayılan olarak ifadenin sonunda uygulanır.
Postgres için SQL Fiddle 9.3. (9.1'de aynı şekilde çalışır.)
Bu sorgulardan birden fazlasını aynı anda yaparsanız , küçük bir yarış durumu vardır . Burada ve burada ve burada ilgili sorular altında daha fazlasını okuyun . Gerçekten de, eğer varsa, ağır eşzamanlı yük altında olur. Başka bir cevapta bildirilen gibi önbellekleme çözümleriyle karşılaştırıldığında, şans çok küçük .
Tekrarlanan kullanım için işlev
Tekrarlanan kullanım için, bir kayıt dizisini parametre olarak unnest(param)
alan ve VALUES
ifadenin yerine kullanılan bir SQL işlevi yaratacağım .
Veya, kayıt dizileri sözdizimi sizin için fazla karışıksa, parametre olarak virgülle ayrılmış bir dize kullanın _param
. Örneğin;
'description1,type1;description2,type2;description3,type3'
Ardından VALUES
yukarıdaki ifadedeki ifadeyi değiştirmek için bunu kullanın :
SELECT split_part(x, ',', 1) AS description
split_part(x, ',', 2) AS type
FROM unnest(string_to_array(_param, ';')) x;
PostGres 9.5’te UPSERT ile işlev
Parametre geçişi için özel bir satır tipi oluşturun. Onsuz yapabiliriz, ama daha basit:
CREATE TYPE foobar AS (description text, type text);
İşlev:
CREATE OR REPLACE FUNCTION f_insert_foobar(VARIADIC _val foobar[])
RETURNS void AS
$func$
WITH val AS (SELECT * FROM unnest(_val)) -- well-known row type
, ins AS (
INSERT INTO foo AS f (type)
SELECT DISTINCT v.type -- DISTINCT!
FROM val v
ON CONFLICT(type) DO UPDATE -- type already exists
SET type = excluded.type WHERE FALSE -- never executed, but lock rows
RETURNING f.type, f.id
)
INSERT INTO bar AS b (description, foo_id)
SELECT v.description, COALESCE(f.id, i.id) -- assuming most types pre-exist
FROM val v
LEFT JOIN foo f USING (type) -- already existed
LEFT JOIN ins i USING (type) -- newly inserted
ON CONFLICT (description) DO UPDATE -- description already exists
SET foo_id = excluded.foo_id -- real UPSERT this time
WHERE b.foo_id IS DISTINCT FROM excluded.foo_id -- only if actually changed
$func$ LANGUAGE sql;
Aramak:
SELECT f_insert_foobar(
'(testing,blue)'
, '(another row,red)'
, '(new row1,purple)'
, '(new row2,purple)'
, '("with,comma",green)' -- added to demonstrate row syntax
);
Eş zamanlı işlem yapılan ortamlar için hızlı ve sağlam.
Yukarıdaki sorulara ek olarak, bu ...
... uygulanır SELECT
veya INSERT
açık foo
: type
FK tablosunda bulunmayan, henüz eklenmiş. Çoğu türün önceden var olduğunu varsayarsak. Kesinlikle emin olmak ve yarış koşullarını elemek için, ihtiyacımız olan mevcut satırlar kilitlenir (böylece eşzamanlı işlemler karışmaz). Bu sizin durumunuz için paranoyaksa, yenisini değiştirebilirsiniz:
ON CONFLICT(type) DO UPDATE -- type already exists
SET type = excluded.type WHERE FALSE -- never executed, but lock rows
ile
ON CONFLICT(type) DO NOTHING
... uygulanır INSERT
veya UPDATE
(gerçek "UPSERT") açık bar
: description
Zaten varsa type
, güncellenir:
ON CONFLICT (description) DO UPDATE -- description already exists
SET foo_id = excluded.foo_id -- real UPSERT this time
WHERE b.foo_id IS DISTINCT FROM excluded.foo_id -- only if actually changed
Ancak, yalnızca type
gerçekten değişirse:
... değerleri iyi bilinen satır türlerinde bir VARIADIC
parametreyle geçirir . Varsayılan maksimum 100 parametreye dikkat edin! Karşılaştırmak:
Birden fazla satır geçmenin birçok yolu var ...
İlgili: