SEN2104 · 5. hafta · Relational Algebra

İlişkisel cebir: birleştirmeler, bölme ve gruplama

Theta, doğal ve dış birleştirmeler, bölme (division), atama ve gruplama (γ) ile çok tablolu sorgular yazmak; “for every” ve “for each” sorularının çözüm kalıpları.

theta joinequijoinnatural joinleft outer joinright outer joinfull outer joindivisionassignmentgeneralized projectionaggregate functiongrouping

Geçen hafta altı temel operatörle her sorguyu yazabileceğini gördün. Ama σ(R×S)\sigma(R \times S) kalıbını her seferinde yazmak hem uzun hem hataya açık. Bu hafta kısaltmalar geliyor: birleştirmeler (join), dış birleştirmeler (outer join), bölme (division) ve gruplama (aggregation). Sınavda en çok puan getiren iki kalıp burada: çok tablolu birleştirme ve “for every” = bölme.

Altın kurallar:

  1. Doğal birleştirme aynı adlı bütün sütunları eşitler. Sadece istediğini değil.
  2. NULL hiçbir değere eşit değildir. İç birleştirmede NULL’lu satır kaybolur; dış birleştirme geri getirir.
  3. “Every / all” gördüğün an bölme düşün.
  4. Gruplama sadece var olan değerlerden grup kurar. Hiç geçmeyen değer 0 ile çıkmaz.

Tablolar geçen haftanın örnek veritabanı: student, course, takes, instructor, department, teaches.

Theta birleştirme ve eşit birleştirme

Theta birleştirme (theta join), çarpım ve seçmenin tek işaretle yazılmış hâlidir:

R⋈θS=σθ(R×S)R \bowtie_{\theta} S = \sigma_{\theta}(R \times S)

θ\theta herhangi bir koşul olabilir (<,≠,≥<, \neq, \ge …). Koşul sadece eşitliklerden oluşuyorsa adı eşit birleştirme (equijoin) olur.

Örnek: aynı bölümde, diğerinden daha çok kazanan hoca çiftleri. Tabloyu kendisiyle birleştirdiğimiz için ρ şart:

πi.name, j.name(ρi(instructor)⋈i.dept_name=j.dept_name∧i.salary>j.salaryρj(instructor))\pi_{i.name,\, j.name}(\rho_{i}(instructor) \bowtie_{i.dept\_name = j.dept\_name \land i.salary > j.salary} \rho_{j}(instructor))

i.namej.name
AksoyDemir
CelikSahin

EE ve BIO’da tek hoca olduğu için çift çıkmaz. >> yerine ≠\neq yazsaydın her çift iki kez gelirdi: (Aksoy, Demir) ve (Demir, Aksoy).

Doğal birleştirme: ⋈

Doğal birleştirme (natural join) R⋈SR \bowtie S üç şey yapar:

  1. R×SR \times S al,
  2. aynı adı taşıyan her sütun çifti için eşitlik seç,
  3. tekrar eden sütunları bir kez bırak.

instructor⋈teachesinstructor \bowtie teaches: ortak sütun sadece inst_id.

inst_idnamedept_namesalarycourse_id
I1AksoyCS95000CS101
I1AksoyCS95000CS305
I2DemirCS80000CS201
I3KayaEE85000EE210
I4SahinMATH70000MA101
I5CelikMATH72000MA202

6 satır, 5 sütun. Yildiz (I6) eşleşmediği için yok. Ortak sütun hiç yoksa doğal birleştirme düz çarpıma dönüşür.

TUZAK · istemediğin ortak sütun. student⋈takes⋈coursestudent \bowtie takes \bowtie course yazınca student.dept_name ile course.dept_name da eşitlenir. Sonuç 14 kayıt değil 9: sadece öğrencilerin kendi bölümünden aldığı dersler kalır. Ada’nın MA101’i, Cem’in MA101 ve CS101’i, Defne’nin CS305’i, Gizem’in her şeyi kaybolur. Çözüm: birleştirmeden önce πcourse_id, title, credits(course)\pi_{course\_id,\, title,\, credits}(course) ile fazla sütunu at, ya da açık bir theta koşulu yaz.

Doğal birleştirme değişmeli ve birleşmelidir: (R⋈S)⋈T=R⋈(S⋈T)(R \bowtie S) \bowtie T = R \bowtie (S \bowtie T). Sütun sırası değişebilir, içerik aynı kalır.

Zincir birleştirme

Galata binasındaki bölümlerin derslerini veren hocalar: teaches → course (course_id) → department (dept_name).

πinst_id(teaches⋈πcourse_id, dept_name(course)⋈σbuilding=’Galata’(department))\pi_{inst\_id}(teaches \bowtie \pi_{course\_id,\, dept\_name}(course) \bowtie \sigma_{building = \text{'Galata'}}(department))

Galata’da CS ve EE var. Bu bölümlerin derslerini I1, I2 ve I3 veriyor.

Dış birleştirmeler

İç birleştirme eşi olmayan satırı atar. Bazen o satır tam olarak istediğin şeydir: “ders vermeyen hocalar da listede görünsün”. Dış birleştirme (outer join) eşleşmeyen satırları korur, eksik sütunları NULL ile doldurur.

İşaretAdKorunan
⟕left outer joinsoldaki eşsiz satırlar
⟖right outer joinsağdaki eşsiz satırlar
⟗full outer joiniki taraftakiler de

instructor ⟕ teaches (sütunların bir kısmı):

inst_idnamecourse_id
I1AksoyCS101
I1AksoyCS305
I2DemirCS201
I3KayaEE210
I4SahinMA101
I5CelikMA202
I6YildizNULL

teaches ⟖ course ise BI100’ü (inst_id = NULL) getirir. Sağ dış birleştirme, tarafları değiştirilmiş sol dış birleştirmedir: R ⟖ S = S ⟕ R (sütun sırası hariç).

student ⟗ department (dept_name üzerinde):

stu_idnamedept_namebuilding
S1AdaCSGalata
S2BoraCSGalata
S3CemEEGalata
S4DefneMATHRumeli
S5EceCSGalata
S6FiratEEGalata
S7GizemNULLNULL
NULLNULLBIOKadikoy

Gizem solda eşsiz kaldı (NULL bölüm), BIO sağda eşsiz kaldı (öğrencisi yok). 6 + 1 + 1 = 8 satır.

TUZAK · SQL lehçeleri. Standart SQL’de FULL OUTER JOIN var. SQLite bunu 3.39 sürümünden beri destekliyor; daha eski SQLite ve MySQL desteklemez. O zaman sol dış birleştirmeyi, sağ taraftaki eşsiz satırlarla UNION yaparak taklit edersin. Bu sayfadaki sonuç iki yolla da doğrulandı.

Bölme: ÷

Bölme (division) şu soruyu cevaplar: “B’deki her değerle eşleşen A değerleri hangileri?” R(A,B)÷S(B)R(A, B) \div S(B) sonucu, SS‘deki her bb için (a,b)∈R(a, b) \in R olan aa‘lardır.

Örnek: bütün CS derslerini alan öğrenciler.

πstu_id, course_id(takes)÷πcourse_id(σdept_name=’CS’(course))\pi_{stu\_id,\, course\_id}(takes) \div \pi_{course\_id}(\sigma_{dept\_name = \text{'CS'}}(course))

Bölen {CS101, CS201, CS305}. Sonuç: S1. Bora CS305’i, Ece CS201 ve CS305’i almadığı için elenir.

Temel operatörlerle türetme

Bölme temel değildir:

R÷S=πA(R)−πA((πA(R)×S)−R)R \div S = \pi_{A}(R) - \pi_{A}((\pi_{A}(R) \times S) - R)

Fikir: herkes eksi “en az bir eksiği olanlar”. Adım adım (T=πstu_id,course_id(takes)T = \pi_{stu\_id, course\_id}(takes), CSCS = CS dersleri):

1) π_stu_id(T)                   = {S1, S2, S3, S4, S5, S7}       aday öğrenciler
2) π_stu_id(T) × CS              = 6 · 3 = 18 satır               herkes her CS dersini alsaydı
3) (2) − T                       = 10 satır                        eksik kalan (öğrenci, ders) çiftleri
     S2-CS305  S3-CS201  S3-CS305  S4-CS101  S4-CS201
     S5-CS201  S5-CS305  S7-CS101  S7-CS201  S7-CS305
4) π_stu_id(3)                   = {S2, S3, S4, S5, S7}           eksiği olanlar
5) (1) − (4)                     = {S1}

Aynı sorguyu SQL’de çift NOT EXISTS ile yazarsın (“almadığı bir CS dersi bulunmayan öğrenci”). 7. haftada göreceksin.

TUZAK · bölünenin sütunları. RR‘de sadece AA ve BB sütunları olmalı. takes÷...takes \div ... yazıp grade’i bırakırsan AA = (stu_id, grade) olur ve sorgu bambaşka bir şey sorar. Bölmeden önce mutlaka π\pi ile daralt.

Atama: ←

Uzun ifadeleri parçalamak için geçici ilişki tanımlarsın:

cs_courses ← π_course_id(σ_{dept_name='CS'}(course))
enrol      ← π_stu_id,course_id(takes)
result     ← enrol ÷ cs_courses

Atama (assignment) yeni güç eklemez; okunabilirlik kazandırır. Sınavda karmaşık sorularda kullanmak puan kaybettirmez, çoğu zaman hata yapmanı engeller.

Genelleştirilmiş izdüşüm

Genelleştirilmiş izdüşüm (generalized projection), π listesinde aritmetik ifadelere izin verir. MATH hocalarına %10 zam:

πname,  salary⋅1.1(σdept_name=’MATH’(instructor))\pi_{name,\; salary \cdot 1.1}(\sigma_{dept\_name = \text{'MATH'}}(instructor))

Sonuç: Sahin 77000, Celik 79200.

Gruplama ve toplu fonksiyonlar: 𝒢

Toplu fonksiyonlar (aggregate functions) bir değer kümesinden tek değer üretir: count, sum, avg, min, max. Gruplama (grouping) işareti:

G1,…,GnGF1(A1),…(E){}_{G_1, \dots, G_n}\mathcal{G}_{F_1(A_1), \dots}(E)

Soldaki alt simge gruplama sütunları, sağdaki toplu fonksiyonlar. Bazı kaynaklar γ\gamma harfini kullanır, anlamı aynı.

Bölüm başına ortalama maaş ve hoca sayısı:

dept_nameGavg(salary), count(inst_id)(instructor){}_{dept\_name}\mathcal{G}_{\text{avg}(salary),\ \text{count}(inst\_id)}(instructor)

dept_nameavg(salary)count(inst_id)
CS875002
EE850001
MATH710002
BIO600001

Gruplama sütunu yazmazsan bütün tablo tek grup olur: Gsum(salary), max(salary)(instructor)\mathcal{G}_{\text{sum}(salary),\ \text{max}(salary)}(instructor) = (462000, 95000).

Kurallar:

  • Toplu fonksiyonlar NULL değerleri atlar. Gcount(dept_name)(student)\mathcal{G}_{\text{count}(dept\_name)}(student) = 6, tabloda 7 satır var.
  • Grup sonucu üzerinde koşul (SQL’deki HAVING) için gruplamanın üstüne σ yazarsın. Bunun için toplu sonuca as ile ad ver: σavg_sal>75000(dept_nameGavg(salary) as avg_sal(instructor))\sigma_{avg\_sal > 75000}({}_{dept\_name}\mathcal{G}_{\text{avg}(salary) \text{ as } avg\_sal}(instructor)) → CS, EE.
  • Girdide hiç geçmeyen bir değer için grup oluşmaz.

Hangi soruya hangi operatör

Sorudaki ifadeKalıp
“… with their department’s building”⋈ (doğal ya da eşit)
“including those who have none”dış birleştirme
“pairs of … in the same …”ρ ile kendi kendine theta birleştirme
“took every / all …”÷
“never / no …”fark
“for each department, the number of …”deptGcount{}_{dept}\mathcal{G}_{\text{count}}
“departments where the average … exceeds”𝒢 sonra σ

Sık yapılan hatalar

Yanlış düşünceDoğrusu
⋈ sadece anahtar sütunu eşitlerAynı adlı bütün sütunları eşitler
student⋈takes⋈coursestudent \bowtie takes \bowtie course bütün kayıtları verirdept_name da eşleşir, 9 satır kalır
Dış birleştirme sonuç satır sayısı = sol tablo satır sayısıBir satır birden çok eşleşebilir; Aksoy iki kez geçer
NULL = NULL eşleşirEşleşmez; Gizem hiçbir bölümle eşleşmedi
“Every” sorusunu ∩ ile çözmekBölen kümenin boyutu değişebilir; ÷ kullan
Bölmeden önce π’yi atlamakFazla sütun AA‘ya karışır, sonuç bozulur
Gruplama 0 sayılı grubu da gösterirHiç geçmeyen değer çıkmaz (BI100 yok)
count(A) satır sayısıdırNULL’ları atlar; satır için count(*)

Kendini test et

Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.

Soru 1kolay

How many tuples are in the result of instructor ⟕ teaches (left outer join on inst_id)?

Cevabı göster
7

İç birleştirme instructor⋈teachesinstructor \bowtie teaches 6 satır verir (Aksoy iki ders verdiği için iki kez geçer). Sol dış birleştirme, eşleşmeyen sol satırları da NULL ile ekler. Ders vermeyen tek hoca I6 (Yildiz): 6 + 1 = 7.

Soru 2kolay

Write a relational algebra expression for the names of instructors who teach CS305. Give the result.

Cevabı göster
π_name(σ_{course_id='CS305'}(instructor ⋈ teaches)) = Aksoy

instructorinstructor ile teachesteaches tek ortak sütun inst_id üzerinden doğal birleşir. Sonra satır koşulu, en son izdüşüm:πname(σcourse_id=’CS305’(instructor⋈teaches))\pi_{name}(\sigma_{course\_id = \text{'CS305'}}(instructor \bowtie teaches)). CS305'i sadece I1 veriyor: Aksoy.

Soru 3orta

How many tuples does student⋈takes⋈coursestudent \bowtie takes \bowtie course (natural joins) return on the sample data? Why is it not 14?

Cevabı göster
9

student⋈takesstudent \bowtie takes ortak sütun stu_id ile 14 satır. Ama course ile ortak sütunlar course_id ve dept_name. Doğal birleştirme ikisini de eşitler, yani sadece öğrencinin kendi bölümünün dersleri kalır: Ada 3, Bora 2, Cem 1 (EE210), Defne 2, Ece 1. Toplam 9. Gizem'in bölümü NULL, hiç eşleşmez. Bütün kayıtları istiyorsan course'u önce πcourse_id,title,credits\pi_{course\_id, title, credits} ile daralt.

Soru 4orta

Write a relational algebra expression for the IDs of students who took every MATH course. Give the result.

Cevabı göster
π_{stu_id,course_id}(takes) ÷ π_course_id(σ_{dept_name='MATH'}(course)) = S4

“Every / all” kalıbı bölmedir. Bölen: MATH dersleri {MA101, MA202}.πstu_id,course_id(takes)÷πcourse_id(σdept_name=’MATH’(course))\pi_{stu\_id, course\_id}(takes) \div \pi_{course\_id}(\sigma_{dept\_name = \text{'MATH'}}(course)).
MA101 alanlar: S1, S3, S4. MA202 alanlar: S4, S7. İkisini birden alan sadece S4.

Soru 5orta

Give the result of course_idGcount(stu_id)(takes){}_{course\_id}\mathcal{G}_{\text{count}(stu\_id)}(takes). Does BI100 appear?

Cevabı göster
CS101 4, CS201 2, CS305 2, EE210 1, MA101 3, MA202 2; BI100 does not appear

takes satırları course_id'ye göre gruplanır, her grupta satır sayılır. CS101: S1, S2, S3, S5 = 4. CS201: 2. CS305: 2. EE210: 1. MA101: S1, S3, S4 = 3. MA202: 2.
Gruplama sadece girdide var olan değerlerden grup üretir. BI100 takes içinde hiç geçmiyor, 0 ile bile çıkmaz. 0 görmek istersen önce dış birleştirme yapmalısın.

Soru 6orta

How many students in CS101 have a grade, i.e. what is Gcount(grade)(σcourse_id=’CS101’(takes)){}\mathcal{G}_{\text{count}(grade)}(\sigma_{course\_id = \text{'CS101'}}(takes))?

Cevabı göster
3

CS101'de 4 kayıt var: S1 (A), S2 (B), S3 (NULL), S5 (F). count(grade) NULL değerleri saymaz: 3. Satır sayısını isteseydin count(stu_id) ya da SQL'de COUNT(*) ile 4 alırdın.

Soru 7orta

Write a relational algebra expression for the departments that have no instructor teaching any course. Give the result.

Cevabı göster
π_dept_name(department) − π_dept_name(instructor ⋈ teaches) = BIO

Ders veren hocaların bölümleri: πdept_name(instructor⋈teaches)\pi_{dept\_name}(instructor \bowtie teaches) = {CS, EE, MATH}. Bütün bölümlerden çıkar: BIO. BIO'nun hocası Yildiz var ama ders vermiyor, bu yüzden sonuçta.

Soru 8orta

How many tuples does student ⟗ department (full outer join on dept_name) have?

Cevabı göster
8

Eşleşen satırlar: bölümü olan 6 öğrenci (S1..S6), her biri kendi bölümüyle. Eşleşmeyen sol satır: Gizem (dept_name NULL, NULL hiçbir şeye eşit değil). Eşleşmeyen sağ satır: BIO (öğrencisi yok). 6 + 1 + 1 = 8.

Soru 9zor

Write a relational algebra expression for the IDs of courses taken by all CS students. Give the result.

Cevabı göster
π_{course_id,stu_id}(takes) ÷ π_stu_id(σ_{dept_name='CS'}(student)) = CS101

Bu sefer “her öğrenci” bölen tarafta: CS öğrencileri {S1, S2, S5}. Bölünen πcourse_id,stu_id(takes)\pi_{course\_id, stu\_id}(takes), sonuç course_id.
CS101'i S1, S2, S5 almış: geçer. CS201'i S5 almamış, CS305'i S2 ve S5 almamış. Sonuç: CS101.

Soru 10zor

Give the result of stu_idGsum(credits)(takes⋈πcourse_id,credits(course)){}_{stu\_id}\mathcal{G}_{\text{sum}(credits)}(takes \bowtie \pi_{course\_id, credits}(course)).

Cevabı göster
S1 15, S2 8, S3 11, S4 10, S5 4, S7 3

Her kayda dersin kredisi eklenir, sonra öğrenci başına toplanır. S1: 4+4+3+4 = 15. S2: 4+4 = 8. S3: 3+4+4 = 11. S4: 4+3+3 = 10. S5: 4. S7: 3. S6 kayıt yaptırmadığı için grubu yok.course'u π\pi ile daralttık. Burada takes ile course arasındaki tek ortak sütun zaten course_id, ama ifadeye student eklendiği an dept_name sorun çıkarır. Daraltmayı alışkanlık edin.

Soru 11kolay

What is Gcount(dept_name)(student){}\mathcal{G}_{\text{count}(dept\_name)}(student)? Compare with the number of tuples in studentstudent.

Cevabı göster
6 (student has 7 tuples)

count(A) sadece NULL olmayan A değerlerini sayar. Gizem'in bölümü NULL, sayılmaz: 6. Tabloda 7 satır var. SQL'de COUNT(dept_name) = 6, COUNT(*) = 7.

Soru 12zor

Write an expression for the departments whose average instructor salary is greater than 75000. Give the result.

Cevabı göster
π_dept_name(σ_{avg_sal>75000}(dept_name G avg(salary) as avg_sal (instructor))) = CS, EE

Önce grupla ve ortalamaya ad ver: dept_nameGavg(salary) as avg_sal(instructor){}_{dept\_name}\mathcal{G}_{\text{avg}(salary) \text{ as } avg\_sal}(instructor) → CS 87500, EE 85000, MATH 71000, BIO 60000.
Sonra grup sonucu üzerinde seçme: σavg_sal>75000\sigma_{avg\_sal > 75000}, en dışta πdept_name\pi_{dept\_name}. Sonuç CS ve EE. Bu, SQL'deki HAVING'in cebirdeki karşılığıdır.

Doğrulama: Bütün sonuç tabloları verify/SEN2104/05.mjs içinde lib.mjs ile (njoin, theta, leftOuter, rightOuter, fullOuter, divide, gamma) hesaplandı; bölme adım adım temel operatörlerle de kuruldu. Her ifadenin SQL karşılığı node:sqlite (SQLite 3.53, FULL OUTER JOIN destekli) üzerinde çalıştırılıp aynı sonuç alındı (56 kontrol, 0 hata).

Deftere not kartı

R ⋈_θ S = σ_θ(R × S) eşitlik varsa equijoin R ⋈ S (natural): AYNI ADLI TÜM sütunları eşitler, ortak sütun bir kez yazılır Ortak sütun yoksa R ⋈ S = R × S TUZAK: student ⋈ takes ⋈ course → dept_name da eşitlenir (14 değil 9 satır) ⟕ sol: eşleşmeyen sol satırlar NULL ile ⟖ sağ ⟗ ikisi de NULL hiçbir şeye eşit değil, NULL'lu satır iç birleştirmede düşer "for every / all" → BÖLME: R(A,B) ÷ S(B) = A'lar ki her B ile eşleşir R ÷ S = π_A(R) − π_A((π_A(R) × S) − R) temp ← ifade (atama): uzun sorguyu parçala π_{name, salary*1.1}: genelleştirilmiş izdüşüm, hesaplanmış sütun G grupla_{fonk}(R): count sum avg min max; NULL'ları atlar Gruplamada boş grup oluşmaz (0 yazan satır yok) HAVING = gruplamanın ÜSTÜNE σ