PostgreSQL'de LATERAL ve bir alt sorgu arasındaki fark nedir?


147

Postgres katılma yeteneği ile ortaya çıktığından LATERALberi, şu anda genel sorguyu dört dakika veya daha fazla süren çok sayıda verimsiz alt sorguyla ekibim için karmaşık veri dökümleri yaptığım için okuyorum.

LATERALKatılmaların bana yardımcı olabileceğini anlıyorum , ancak Heap Analytics'ten böyle bir makale okuduktan sonra bile hala takip etmiyorum.

Bir LATERALbirleştirmenin kullanım durumu nedir ? LATERALJoin ve subquery arasındaki fark nedir ?


Yanıtlar:


163

Daha çok ilişkili bir alt sorgu gibi

Bir LATERAL(Postgres 9.3 veya üzeri) daha fazla gibi birleştirme ilişkili alt sorgu , bir düz alt sorgu. Andomar'ın işaret ettiği gibi , LATERALbirleştirmenin sağındaki bir işlev veya alt sorgu, soldaki her satır için bir kez değerlendirilmelidir - tıpkı ilişkili bir alt sorgu gibi - düz bir alt sorgu (tablo ifadesi) yalnızca bir kez değerlendirilir . (Sorgu planlayıcısının,
her ikisi için de performansı optimize etmenin yolları vardır.) Bu ilgili yanıt, aynı sorunu çözerek her iki taraf için de kod örneklerine sahiptir:

Birden fazla sütun döndürmek için bir LATERALbirleştirme genellikle daha basit, daha temiz ve daha hızlıdır.
Ayrıca, ilişkili bir alt sorgunun eşdeğeri olduğunu unutmayın LEFT JOIN LATERAL ... ON true:

Üzerindeki kılavuzu okuyun LATERAL

Burada cevaplara koyacağımız her şeyden daha otoriter:

Bir alt sorgunun yapamayacağı şeyler

Orada olan bir bu işler LATERALyapabilir katılmak, ancak bir (korelasyon) alt sorgu (kolayca) olamaz. İlişkili bir alt sorgu, çıplak işlev çağrıları (birden çok satır döndürürlerse sonuç satırlarını çoğaltır) hariç, yalnızca tek bir değer döndürür, birden çok sütun değil, birden çok satır döndüremez. Ancak bazı set-döndürme işlevlerine bile sadece FROMmaddede izin verilir . unnest()Postgres 9.4 veya sonraki sürümlerde çoklu parametrelerde olduğu gibi . Kullanım kılavuzu:

Buna sadece FROMmaddede izin verilir ;

Bu işe yarar, ancak kolayca bir alt sorgu ile değiştirilemez:

CREATE TABLE tbl (a1 int[], a2 int[]);
SELECT * FROM tbl, unnest(a1, a2) u(elem1, elem2);  -- implicit LATERAL

Madde içindeki virgül ( ,) FROMkısa gösterimdir CROSS JOIN.
LATERALtablo işlevleri için otomatik olarak kabul edilir.
Özel durum hakkında daha fazla bilgi UNNEST( array_expression [, ... ] ):

SELECTListedeki geri dönen işlevler

Ayrıca gibi set dönen işlevleri kullanabilirsiniz unnest()içinde SELECTdoğrudan listeye. Bu, aynı SELECTlistede Postgres 9.6'ya kadar birden fazla işlevle şaşırtıcı davranışlar sergilemek için kullanılır . Ancak sonunda Postgres 10 ile sterilize edildi ve şu anda geçerli bir alternatif (standart SQL olmasa bile). Görmek:

Yukarıdaki örneğe dayanarak:

SELECT *, unnest(a1) AS elem1, unnest(a2) AS elem2
FROM   tbl;

karşılaştırma:

pg 9.6 için dbfiddle burada
pg 10 dbfiddle burada

Yanlış bilgileri netleştirin

Kullanım kılavuzu:

İçin INNERve OUTERbirleştirme türleri, bir durumdur, yani tam olarak biri belirtilmelidir katılması NATURAL, ON join_condition veya USING( join_column [...]). Anlam için aşağıya bakın.
Çünkü CROSS JOIN, bu maddelerin hiçbiri görünmez.

Dolayısıyla bu iki sorgu geçerlidir (özellikle yararlı olmasa bile):

SELECT *
FROM   tbl t
LEFT   JOIN LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t ON TRUE;

SELECT *
FROM   tbl t, LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t;

Bu olmasa da:

SELECT *
FROM   tbl t
LEFT   JOIN LATERAL (SELECT * FROM b WHERE b.t_id = t.t_id) t;

Bu yüzden Andomar en @ kod örneği doğrudur ( CROSS JOINbir birleşim koşulu gerektirmez) ve Attila'nın @ DİR geçersiz oldu.


Bir alt sorgunun LATERAL JOIN'in yapamayacağı bazı şeyler vardır. Pencere fonksiyonları gibi. Burada olduğu gibi
Evan Carroll

@EvanCarroll: Bağlantıda herhangi bir ilişkili alt sorgu bulamadım. Ancak bir LATERALalt sorguda bir pencere işlevini göstermek için başka bir cevap ekledim : gis.stackexchange.com/a/230070/7244
Erwin Brandstetter

1
Daha temiz ve daha hızlı? Bazı durumlarda büyüklükleri daha hızlı seviyorum. LATERAL'a geçtikten sonra günler ve saniyeler arasında geçen bir sorgu vardı.
rovyko

52

Bir olmayan lateralve lateralbirleştirme arasındaki fark , sol taraftaki tablonun satırına bakıp bakamayacağınızdır. Örneğin:

select  *
from    table1 t1
cross join lateral
        (
        select  *
        from    t2
        where   t1.col1 = t2.col1 -- Only allowed because of lateral
        ) sub

Bu "dışa dönük", alt sorgunun birden çok kez değerlendirilmesi gerektiği anlamına gelir. Sonuçta, t1.col1birçok değer alabilir.

Bunun aksine, bir lateralbirleşmeden sonraki alt sorgu bir kez değerlendirilebilir:

select  *
from    table1 t1
cross join
        (
        select  *
        from    t2
        where   t2.col1 = 42 -- No reference to outer query
        ) sub

Olmadan gerektiği gibi, lateraliç sorgu dış sorguya hiçbir şekilde bağlı değildir. Bir lateralsorgulama bir örneğidir correlated, çünkü terimi kendisi dışında sıraları ile ilişkisi, sorgu.


5
Bu yanal birleşmenin en temiz açıklamasıdır.
1valdis

anlaşılması kolay açıklama, teşekkür ederim.
arilwan

nasıl select * from table1 left join t2 using (col1)karşılaştırır? / On koşulunu kullanan bir birleştirmenin yetersiz olduğu ve yanal kullanmanın daha mantıklı olacağı benim için belirsizdir.
No_name

9

İlk olarak, Lateral ve Cross Apply aynı şeydir . Bu nedenle Çapraz Uygulamayı da okuyabilirsiniz. SQL Server'da çağlar boyunca uygulandığından, bu konuda Lateral'dan daha fazla bilgi bulacaksınız.

İkincisi, anlayışım gereği, yan sorgu kullanmak yerine alt sorgu kullanarak yapamayacağınız hiçbir şey yok. Fakat:

Aşağıdaki sorguyu düşünün.

Select A.*
, (Select B.Column1 from B where B.Fk1 = A.PK and Limit 1)
, (Select B.Column2 from B where B.Fk1 = A.PK and Limit 1)
FROM A 

Bu durumda yanal kullanabilirsiniz.

Select A.*
, x.Column1
, x.Column2
FROM A LEFT JOIN LATERAL (
  Select B.Column1,B.Column2,B.Fk1 from B  Limit 1
) x ON X.Fk1 = A.PK

Bu sorguda, sınır yan tümcesi nedeniyle normal birleştirme kullanamazsınız. Yanal veya Çapraz Uygula, basit birleştirme koşulu olmadığında kullanılabilir .

Yanal veya çapraz uygulama için daha fazla kullanım vardır, ancak bu en yaygın olanıdır.


1
Kesinlikle, ben PostgreSQL kullanan merak lateralyerine apply. Belki Microsoft sözdizimini patentledi?
Andomar

9
@Andomar AFAIK lateralSQL standardındadır ancak applydeğildir.
mu çok kısa

Bu LEFT JOINbirleştirme koşulu gerektirir. ON TRUEBir şekilde kısıtlamak istemiyorsanız bunu yapın .
Erwin Brandstetter

Erwin haklıysa, a cross joinveya onkoşul kullanmazsanız hata alırsınız
Andomar

1
@Andomar: Bu yanlış bilgilendirmeden dolayı açıklığa kavuşturmak için başka bir cevap ekledim.
Erwin Brandstetter

4

Kimsenin işaret etmediği bir şey, LATERALseçilen her satıra kullanıcı tanımlı bir işlev uygulamak için sorguları kullanabilmenizdir .

Örneğin:

CREATE OR REPLACE FUNCTION delete_company(companyId varchar(255))
RETURNS void AS $$
    BEGIN
        DELETE FROM company_settings WHERE "company_id"=company_id;
        DELETE FROM users WHERE "company_id"=companyId;
        DELETE FROM companies WHERE id=companyId;
    END; 
$$ LANGUAGE plpgsql;

SELECT * FROM (
    SELECT id, name, created_at FROM companies WHERE created_at < '2018-01-01'
) c, LATERAL delete_company(c.id);

PostgreSQL'de böyle bir şeyi nasıl yapacağımı bilmemin tek yolu bu.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.