(RDB) Join’i Anlamanın 13 Yolu
(justinjaffray.com)- İlişkisel veritabanlarındaki inner join, basit bir SQL söz diziminin ötesinde; sorgulama, iç içe döngüler, mantıksal model, tip denetimi ve cebir açısından aynı yapının farklı yorumları olarak görülebilir
- Normalize edilmiş tablolarda join, referansları izleyerek tekrarsız saklanan bilgileri yeniden birleştiren en pratik araç hâline gelir
- Uygulama açısından, satır çiftleri üzerinde dolaşıp yalnızca koşulu sağlayan kombinasyonları bırakmak ya da sütun domain’lerinde iki ilişkide de bulunan değer kombinasyonlarını seçmek olarak görülebilir
- Programlama modelinde join;
flatMap, SQLLATERAL, ORM’lerde N+1 problemi çözümü, Rust trait tabanlı tip denetimi ve Set monad’ınınandThenişlemiyle açıklanabilir - Matematiksel olarak graf yolları, en küçük model, izin verilen en büyük ilişki, kısmi sıralamanın en küçük üst sınırı ve ilişkisel ifadelerin halka çarpımı, join’in aynı özelliklerini ortaya koyar
Normalize edilmiş veride join bir sorgulamaya dönüşür
- Join, en pratik anlamıyla belirli bir değeri sorgulamak ya da mevcut veriye tekrarlı bilgiyi eklemek olarak görülebilir
- Örnek,
user,country,country_codedeğerlerini tek bir tabloda saklama yaklaşımıyla başlar- Aynı
countrydeğerinin her geçtiği yerdecountry_codetekrarlandığı için yinelenme oluşur - Değerleri sık değişen verilerde tüm konumları birlikte güncellemek gerektiğinden hata ve verimsizlik artar
- Aynı
- Normalize edilmiş biçimde
countryilecountry_codearasındaki ilişki ayrı bir tabloya ayrılır; kullanıcı tablosu ise yalnızcacountry_iddeğerine referans verir usersvecountriestablolarıcountry_idüzerindenINNER JOINedilirse, başlangıçtakiuser,country,country_codebiçimi yeniden elde edilir- Sonraki açıklamalar, aynı adlı sütunlara göre örtük olarak join yapıldığını varsayar; ancak ayrıntılı SQL söz dizimine sıkı sıkıya bağlı kalmaz
Uygulama açısından: satır ve sütunlar üzerinde dolaşan join
- İki küme
R,Sve bir yüklempolduğunda join, tümr ∈ R,s ∈ Sdeğerleri üzerinde dolaştıktan sonra yalnızcap(r, s)doğru olanları çıktı olarak üretir- İki koleksiyonun Kartezyen çarpımı olası tüm satır bağlantılarıysa, join bunun içinde koşulu sağlayan alt kümedir
- Sütun merkezli bakıldığında, her sütunun domain’i olası değerler kümesi olarak alınır ve sütun değer kombinasyonları üzerinde dolaşılır
R(a, b)veS(b, c)varsaa,b,cdomain’leri üzerinde dolaşılır(a, b)Riçinde ve(b, c)Siçinde olduğunda yalnızca[a, b, c]çıktılanır
Uyumlu alternatif gerçeklikler olarak join
- John ve Sally örneği, tarafların bilginin yalnızca bir kısmına sahip olduğu bir durumda yalnızca uyumlu gerçekliklerin bırakılması üzerinden join’i açıklar
- John, kendi evcil hayvanı ile sokak hayvanının olası kombinasyonlarını bilir; Sally de kendi evcil hayvanı ile sokak hayvanının olası kombinasyonlarını bilir
- John’un köpeği olduğunda sokak hayvanının köpek olması ile Sally’nin kedisi olduğunda sokak hayvanının fare olması aynı anda doğru olamaz
- Çünkü iki kişinin aynı sokak hayvanını gözlemlemesi gerekir
- İki tablo
strayüzerinden join edilirse, John’un evcil hayvanı, sokak hayvanı ve Sally’nin evcil hayvanı kombinasyonları içinde yalnızca birbirleriyle çelişmeyen durumlar kalır
Programlama modelinde join
flatMap, başlangıçtaki dizinin her öğesi için yeni bir dizi oluşturup sonuçları ardışık olarak birleştiren bir fonksiyondur ve join’i uygulamak için kullanılabilirSELECT * FROM r INNER JOIN s ON p,r.flatMap(x => s.filter(y => p(x, y)))olarak ifade edilir- SQL’in bazı varyantlarındaki
LATERALsöz dizimi, join’iflatMapbiçimine dönüştürür
LATERALifadesinin sağ tarafı soldaki sütunlara referans vermiyorsa Kartezyen çarpım ile eşdeğerdir- Sorgu decorrelation işlemi, sağ taraftaki sütun referanslarını ardışık yeniden yazımlarla kaldırma yaklaşımına dayanır
- ORM’lerde yaygın olan N+1 problemi de join ile açıklanabilir
- Sonuç kümesindeki her satır için ek bir sorgu çalıştırıldığında, Postgres gibi bağlantı kullanan veritabanlarında her tekil sorgunun sabit maliyeti yüksektir
- Veritabanından “bu sorgulamaların hepsini gerçekleştir” diye istemenin sonucu,
users INNER JOIN countriesgibi bir join’dir - Sqlite gibi in-process veritabanlarında bu problem daha hafiftir
Graf yolları ve mantıksal model
- İlişki, iki kümeyi “ilişkilendirdiği” için graf olarak görülebilir
userstablosu, kullanıcı adı kümesi ilecountry_idkümesini birbirine bağlarcountry_idile iki harfli ülke kodlarını bağlayan ilişki de ayrı bir graf olarak gösterilebilir
- İlk grafın sağdaki kümesi ile ikinci grafın soldaki kümesi aynı vertex setini paylaşıyorsa bunlar tek bir graf olarak birleştirilebilir
- Soldaki kümeden başlayıp ortadaki vertex üzerinden sağdaki kümeye giden tüm yollar listelenirse, iki ilişkinin join’i elde edilir
- Biçimsel mantıkta ilişkiler yüklem olarak, cümleler kümesini doğru yapan olgular kümesi ise model olarak görülür
users(A, B)vecountries(B, C, D)doğruysaQ(A, B, C, D)doğrudur şeklinde bir çıkarım konur- Bu koşulu sağlayan birden fazla model olabilir
- Standart sonucu elde etmek için koşulu sağlayan modeller arasından en küçük model seçilir
- Bu en küçük model,
usersilecountrytablolarının join sonucuyla aynıdır
Tip denetimi olarak join
- ML tarzı tip sistemleri Prolog ve Datalog’a güçlü biçimde benzediği için join’e benzer şekilde ifade edilebilir
- Rust örneğinde ilişkiler trait olarak tanımlanır
UsersveCountryCodeilişki rolünü üstlenirSmudge,Sissel,Petee,Canada,UnitedStates,CA,USsomut tipler olarak tanımlanır
(Smudge, Canada): Users,(Canada, CA): CountryCodegibi trait implementasyonları ilişkinin satırlarına karşılık gelir(A, B, C)değerinin join’e dahil olması için(A, B): Usersve(B, C): CountryCodeolmalıdırtest::<(Smudge, _, CA)>()tip denetiminden geçer; ancaktest::<(Smudge, _, US)>(),(Canada, US): CountryCodeimplementasyonu olmadığı için başarısız olur
Set monad işlemi olarak join
- JavaScript’teki
SomeveNoneörneği, optional record’ları birleştirme yöntemiyle başlar- İki record aynı
countrydeğerine sahipse birleştirilipSomedöndürülür - Uyumlu değillerse ya da değer yoksa
Nonedöndürülür
- İki record aynı
andThen, optional değerin içini çıkarıp birleştirme fonksiyonunu uygular- Aynı
combinefonksiyonu korunup containerRelolarak değiştirilirse ilişki kümeleri işlenebilirRel.map, tüm satırlara fonksiyon uygularRel.andThen, her satırdan çıkan ilişkiyiflatMapile ardışık olarak birleştirir
usersilişkisi ilecountriesilişkisine aynıcombineçalıştırıldığında,Smudge,Sissel,Peteedeğerlerine ülke kodu eklenmiş join sonucu elde edilir
İzin verilen en büyük ilişki ve kısmi sıralamanın join’i
- İki ilişki
R,Siçindeki tüm sütunlara sahip üçüncü bir ilişkiT, yeni bilgi icat etmiyorsa izin verilebilir olarak tanımlanırTiçindeki herhangi bir satırRsütunlarıyla sınırlandığında bu satırRiçinde bulunmalıdır- Aynı şekilde
Ssütunlarıyla sınırlandığında da bu satırSiçinde bulunmalıdır
- Örneğin
Smudge, Canada, USizin verilebilir değildir- Yalnızca
country,country_codeaçısından bakıldığındaCanada, USolur; ancak buSiçinde olmayan bir satırdır
- Yalnızca
- Boş ilişki de izin verilebilirdir; ancak izin verilen en büyük ilişki
Smudge-Canada-CA,Sissel-Canada-CA,Petee-United States-USdeğerlerini içerir - Bu izin verilen en büyük ilişki, iki ilişkinin join’idir
- Kısmi sıralama açısından
R ≤ Qşu şekilde tanımlanırQ,Riçindeki tüm sütunları içerirQiçindeki her satırRsütunlarıyla sınırlandığındaRsatırı olur
- Bu kısmi sıralamada iki ilişki
R,Siçin en küçük üst sınır olanR ∨ Svardır; bu da ilişkisel join ile aynı anlama gelen join’dir
Halka çarpımı olarak join
- İlişkiler cebirsel olarak da ifade edilebilir
- Tek bir satır, sütun-değer çiftlerinin çarpımı olarak gösterilir
- Tek bir ilişki, birden fazla satırın toplamı olarak gösterilir
- Örneğin
user = Smudgeilecountry_id = 1değerlerinin çarpımı olan terim tek bir satır olur - İfadeyi sadeleştirmek için kurallar eklenir
- Idempotence:
[x = y][x = y] = [x = y] - Contradiction:
[x = y][x = z] = 0ify ≠ z
- Idempotence:
- Kullanıcı ilişkisi
Rile ülke lookup ilişkisiSçarpılıp dağılım ve değişme yasalarıyla açıldığında, çelişen terimler kaybolur ve yalnızca uyumlu terimler kalır - Kalan ifade
Smudge-1-Canada-CA,Sissel-1-Canada-CA,Petee-2-United States-USolur; bu da tam olarak iki ilişkinin join’idir - Bu yöntem tensor contraction olarak da görülebilir
1 yorum
Hacker News görüşleri
join kavramını uzamsal boyutlar üzerinden düşünmeye başlayınca anlamak çok daha kolaylaştı
Her boyutu
Dim_X,Dim_Y,Dim_Zgibi ayrı tablolarda tutup aynıEntityIdile bağlayınca, bunu tek bir varlığın 3 boyutlu konumunu oluşturmak gibi görmek mümkün3 boyut oluşturmak için en az 2 inner join gerekir; zaman gibi uzamsal olmayan boyutlar da aynı şekilde genişletilebilir
Belirli bir zamanı kısıtlamazsanız, bu bir varlığın zaman içinde bulunduğu tüm konumları içeren bir rapora dönüşür
Diğer join türleri de, şemayı zihninizde “döndürebilecek” kadar kavradığınızda bu varyasyon üzerinden daha kolay anlaşılır hale geliyor
https://dbdb.io/db/hyperdex
EntityPosition(EntityId, X, Y, Z)gibi bir tabloda tutardımYine de farklı boyut parçalarını birleştirme ve agregasyonlarla uğraşma tarafı veri ambarı dünyasını çağrıştırıyor
JOIN,INNER JOINgibi sözdizimleri kullandığımızı hep merak etmişimdir. TablolarıFROMiçinde listeleyip join koşullarınıWHEREbölümünde denklem gibi yazmak bana çok daha açık geliyorKarmaşık
FROMbölümlerinde birden çokJOINkarışınca okumak zorlaşıyor; eşitlik koşullarınıWHEREiçinde okumak daha sezgisel geliyorhttps://en.m.wikipedia.org/wiki/Relational_algebra
Doğal join
R ⋈ S, ortak öznitelik adları aynı olan tuple kombinasyonlarının kümesidir ve mantıksalAND'ye karşılık gelen ilişkisel işlemdir⋈, sonuca girmemesi gereken satırları bir yüklemle filtreleyen Kartezyen çarpım olarak görülebilir; SQL'in büyük bir kısmı bu açıdan bakınca iyi anlaşılıyorBirkaç gündür query execution / planning implementation materyali arıyorum ama yüklem, mevcut indeksler ve join gibi uygulama tarafına dair kaynak bulmak zor
Google arama sonuçları kullanım odaklı içeriklerle kirlenmiş durumda
Şimdiye kadar yalnızca CMU Database Group materyallerini bulabildim; onlar da gerçekten mükemmel
https://pi3.informatik.uni-mannheim.de/~moer/querycompiler.p...
TUM'un “Database Systems on Modern CPU Architectures” dersi de yardımcı olabilir; 2020 materyallerinde tam video dersler de var
https://db.in.tum.de/teaching/ss20/moderndbs/?lang=en
https://www.sqlite.org/optoverview.html
https://www.sqlite.org/queryplanner.html
https://www.postgresql.org/docs/current/planner-optimizer.ht...
Query execution ve query planning aslında neredeyse ayrı konular
Join optimizasyonu makaleleri arasında hâlâ orijinal Selinger makalesinin en iyisi olduğunu düşünüyorum
https://courses.cs.duke.edu/compsci516/cps216/spring03/paper...
Outer join desteği yok ve daha verimli teknikler geliştirildi, ama System R tarzı optimizer'lara bakan biri için hâlâ çok tanıdık geliyor
Postgres'in
src/backend/optimizer/READMEdosyasında da başka yerde zor bulunacak çok şey varCMU'dan Andy Pavlo'nun dersleri, bu konuları internette anlatan neredeyse tek kaynak sayılır; “Building Query Compilers” PDF'i eksik olsa da Moerkotte ve diğerlerinin temel makalelerini içerdiğinden modern uygulamalar için bakmaya değer
Uygulanabilir indeksleri bulmak genelde sargable predicate olup olmadığına bakılarak yapılabildiği için çok zor değil, ama seçicilik tahmini zordur ve join sonrasındaki seçicilik tahmini optimizer'ın en zor problemlerinden biridir
Örneğin
A=x AND B=y AND C=zvarsa ve yalnızca(A,B),(B,C)indekslerinin seçicilik / cardinality bilgileri varsa, üç koşulun birlikte seçiciliğinin nasıl tahmin edileceği hiç de basit değildirBunu çözmek için “second-order cone programming” çözücüsü gerektiren makaleler bile var
query planningaratınca, kabaca bakınca bile uygulama tarafına dönük daha fazla sonuç çıkıyorBu, tabloları ikişer ikişer join’leyip sürekli ara sonuçlar üretmek yerine, 3 veya daha fazla tabloyu ara sonuç olmadan birlikte join’lemek anlamına geliyor
İlgili blog yazısı ve kısa video https://relational.ai/blog/dovetail-join adresinde, orijinal makale ise https://dl.acm.org/doi/pdf/10.1145/3180143 adresinde
RelationalAI'da çalışıyorum; akademide yaklaşık 10 yıldır araştırılan bu yeni join algoritmasını biz ve birkaç başka yeni veritabanı şirketi pazara taşıyoruz
https://justinjaffray.com/a-gentle-ish-introduction-to-worst...
AND,NORolur ve Tetris bundan yararlanırEn kötü durum sınırı, durumsuz/streaming WCOJ’a kıyasla daha da sıkılaşmıyor, ancak gerçek veriler çoğu zaman çok daha küçük box certificate’lara sahip oluyor
Dovetail join’in özyinelemeli sorguları, yani yalnızca çıktı ilişkisini belirtip ara ilişkileri motorun kendisinin yönettiği keyfi datalog’u destekleyip desteklemediğini görmedim
Bu tür sorguları destekleyip desteklemediğini merak ediyorum
İlişkisel modelin inceliklerini özellikle uygulama seviyesi geliştiricilere gösteren bu tür yazılar daha fazla olmalı
Fonksiyonel programlama bakış açısından yapılan açıklama ve inceleme de kısa ve ikna edici
N+1 problemini öğretmek için bir fırsat daha kaçırılmış gibi görünüyor
Cluster edilmemiş bir indekse join yapmak da hâlâ N+1’dir; sadece ağ ve disk arasında gidip gelen N+1 değil, disk üzerindeki N+1’dir
Inner join, koşullu bir Kartezyen çarpımdır
Eşitlik join koşuluna sahip inner join’ler koşulu doğrudan üretir; eşitsizlik join koşulları ise gerçek değerlendirme gerektirir
Güzel bir açıklama. “Doğru yöntem tabloları normalize etmektir” sözü işlemsel veritabanları için doğru, ancak veri ambarlarında belli ölçüde denormalizasyon yaygın biçimde kabul görüyor
Normalizasyon örneğini görünce, tabloları string yerine sayısal birincil anahtarların daha hızlı olduğunu düşünerek tasarladığımız günleri hatırladım
Sonra anlamsız
idalanları ortaya çıkıyordu ve aslında istediğiniz benzersiz değeri elde etmek için join gerekiyorduBir gün, iki tabloda aynı benzersiz anahtarı kullanmanın join sayısını azaltabileceğini fark ettim; basitti ama etkiliydi
idalanı bulundurmayı seviyorum. Loglama için yardımcı oluyor ve birden fazla alandan oluşan “gerçek” anahtarla uğraşmak gerekmiyorBunun yerine string değerler üzerinde benzersiz indeks tutuyor ve daha da önemlisi bütünlük kısıtlarını oraya koyuyorum
Anlamlı string’lerle dolu tablolar, sayısal
idya da UUID’lerle dolu tablolardan çok daha okunaklıdır