← Database Management System Ana Sayfa
İlişkisel Cebir (Relational Algebra) — Konu Anlatımı
Veritabanı Sistemleri • Ders Notları

İlişkisel Cebir & SQL

Relational Algebra operatörlerinden SQL sorgularına — kapsamlı, adım adım konu anlatımı. Üniversite veritabanı ve uçuş veritabanı örnekleriyle.

6Tek-tablo operatörü
6İki-tablo operatörü
5SQL kategorisi
11Örnek sorgu (Uçuş DB)
Başla
01

Temel Kavramlar & Relational Algebra'ya Giriş

Temel Veritabanı Terminolojisi

İlişkisel veritabanlarını anlamak için dört temel kavramı bilmek gerekir:

🗂️
Tablo / Dosya (Table / File)
Aynı yapıdaki satırların toplandığı koleksiyon. İlişkisel modelde her tablo bir ilişkiyi (relation) temsil eder.
📄
Kayıt / Satır (Record / Row)
Tablodaki tek bir varlığı (entity) temsil eden satır. Her satır, bir kaydın tüm öznitelik değerlerini içerir.
📌
Sütun / Öznitelik (Column / Attribute)
Her sütun, tablodaki bir özelliği temsil eder. Sütunun veri türü ve adı şema aşamasında belirlenir.
🔑
Anahtar (Key) & Dış Anahtar (Foreign Key)
Key constraint: Tablodaki her satırı benzersiz tanımlayan sütun(lar). Foreign Key / Referential Integrity: Bir tablodaki sütunun başka tablonun birincil anahtarına referans vermesi.

Relational Algebra Nedir?

Relational Algebra (İlişkisel Cebir), SQL gibi gerçek sorgu dillerinin temelini oluşturan matematiksel bir dildir. Her SQL sorgusunun karşılık gelen bir relational algebra sorgusu vardır.

ℹ️
Temel prensip: Bir relational algebra sorgusu operatörlerden oluşur. Her operatör bir veya daha fazla tabloyu girdi alır, tek bir tablo üretir. Operatörlerin bu zincirleme kompozisyonu query tree (sorgu ağacı) olarak gösterilebilir.
Tek-Tablo Operatörleri
Select Project Sort Rename Extend GroupBy
İki-Tablo Operatörleri
Product Join Semijoin Antijoin Union Outer Join
02

Üniversite Veritabanı Şeması

Örneklerimizde kullanılan üniversite veritabanı 5 tablodan oluşmaktadır. Altı çizili alanlar birincil anahtar, renkli alanlar ise dış anahtardır.

STUDENT (SId, SName, GradYear, MajorId)
DEPT (DId, DName)
SECTION (SectId, CourseId, YearOffered, Prof)
COURSE (CId, Title, DeptId)
ENROLL (EId, Grade, StudentId, SectionId)
🔗
Dış anahtar ilişkileri: STUDENT.MajorId → DEPT.DId  |  SECTION.CourseId → COURSE.CId  |  COURSE.DeptId → DEPT.DId  |  ENROLL.StudentId → STUDENT.SId  |  ENROLL.SectionId → SECTION.SectId

Örnek veriler: 9 öğrenci, 3 bölüm, 6 ders, 5 section ve 6 kayıt mevcuttur. Bu veriler üzerinden tüm operatörleri adım adım inceleyeceğiz.

03

Select Operatörü

Tanım

select(input_table, predicate)

Select operatörü, girdi tablosunun aynı sütunlarını korurken sadece belirtilen koşulu sağlayan satırları döndürür. Yani sütun sayısı değişmez; yalnızca bazı satırlar elenir.

📌 Görsel: Tablonun bazı satırları üzerine çarpı (✗) konulur — o satırlar çıktıya gelmez.
Örnekler
Q1 = select(STUDENT, GradYear=2004)
→ Mezuniyet yılı 2004 olan öğrenciler

Q2 = select(STUDENT, GradYear=2004 and (MajorId=10 or MajorId=20))
→ 2004'te mezun olup majoru 10 veya 20 olan öğrenciler

-- İç içe select (nested select) ile aynı sonuç:
Q3 = select(select(STUDENT, GradYear=2004), MajorId=10 or MajorId=20)
Q4 = select(Q1, MajorId=10 or MajorId=20)
→ Q2 = Q3 = Q4 (hepsi aynı sonucu verir)

Q3 için sorgu ağacı (query tree):

Select: MajorId = 10 or MajorId = 20
Select: GradYear = 2004
STUDENT
Önemli: Select operatöründe and, or, not mantıksal operatörleri ve =, <, >, <=, >= karşılaştırma operatörleri kullanılabilir. İç içe select'ler her zaman tek bir select'teki and ile eşdeğerdir.
04

Project Operatörü

Tanım

project(input_table, {field_name(s)})

Project operatörü, girdi tablosunun tüm satırlarını korurken sadece belirtilen sütunları döndürür. Yani satır sayısı değişmez; yalnızca bazı sütunlar elenir.

📌 Görsel: Tablonun bazı sütunları koyu renkte gösterilir — yalnızca o sütunlar çıktıya gelir.
Örnekler
Q5 = project(STUDENT, {SName, GradYear})
→ Tüm öğrencilerin sadece ismi ve mezuniyet yılı

Q6 = project(select(STUDENT, MajorId=10), {SName})
→ Majoru 10 olan öğrencilerin isimleri

Q6 için sorgu ağacı:

Project: {SName}
Select: MajorId = 10
STUDENT
⚠️
Hatalı sıralama: Q7 = select(project(STUDENT, {SName}), MajorId=10) Bu sorgu geçersizdir! Çünkü iç sorguda (project) sadece SName sütunu kalmıştır. MajorId sütunu artık mevcut olmadığından dıştaki select bunu bulamaz ve hata verir.

Kural: Select her zaman Project'ten önce uygulanmalıdır (eğer select koşulunda kullanılan alan project'te elenmişse).
05

Sort Operatörü

Tanım

sort(input_table, [field_name(s)])

Sort operatörü, girdi tablosunun tüm sütunlarını ve satırlarını aynen korur; yalnızca satırların sırasını değiştirir. Köşeli parantez [ ], sıralama alanlarının sıralı listesini belirtir. İlk alan birincil sıralama, ikincisi ikincil sıralama kriteridir.

Örnek
Q8 = sort(STUDENT, [GradYear, SName])
→ Öğrenci kayıtları önce mezuniyet yılına (GradYear),
sonra isme (SName) göre sıralanır.
ℹ️
Sort operatörü bir ara sonuç olarak nadiren tek başına kullanılır; genellikle sorgunun en sonunda son çıktıyı sıralamak için uygulanır. SQL'deki karşılığı ORDER BY cümlesidir.
06

Rename Operatörü

Tanım

rename(input_table, field_name, new_field_name)

Rename operatörü, girdi tablosunu olduğu gibi döndürür; yalnızca belirtilen bir sütunun adını değiştirir. Satır sayısı, sütun sayısı ve veriler değişmez.

Örnek
Q9 = rename(Q6, SName, CSMajors)
→ Q6'daki SName sütunu CSMajors olarak yeniden adlandırılır.
Çıktı: tek sütunlu bir tablo, başlığı "CSMajors"
💡
Rename, özellikle Union ve self-join operasyonlarında kritik öneme sahiptir. Union iki tablonun aynı şemaya sahip olmasını gerektirdiğinden, farklı isimli sütunları rename ile eşleştirmek gerekebilir. Ayrıca aynı tablonun iki kopyasını join ederken çakışan sütun adlarını ayırt etmek için de kullanılır.
07

Extend Operatörü

Tanım

extend(input_table, expression, new_field_name)

Extend operatörü, girdi tablosunu olduğu gibi alır ve bir hesaplama ifadesi (expression) sonucunu içeren yeni bir sütun ekleyerek döndürür. Mevcut sütunlar ve satırlar değişmez; tabloya yeni bir alan eklenir.

Örnekler
Q10 = extend(STUDENT, GradYear-1863, GradClass)
→ Her öğrenci için GradYear - 1863 hesaplanır,
sonuç GradClass adlı yeni sütuna yazılır.

Q11 = extend(STUDENT, 'BC', College)
→ Tüm öğrencilerin College alanına sabit 'BC' değeri atanır.
💡
Expression; aritmetik işlemler (GradYear-1863), sabit değerler ('BC'), string birleştirme veya diğer sütun değerlerini kullanan hesaplamalar olabilir. SQL'deki karşılığı SELECT cümlesindeki türetilmiş sütunlardır (ör. SELECT GradYear-1863 AS GradClass).
08

GroupBy Operatörü

Tanım

groupby(input_table, {grouping_fields}, {aggregation_expressions})

GroupBy operatörü, girdi tablosunun satırlarını belirtilen alanlara göre gruplara ayırır ve her grup için bir çıktı satırı üretir. Gruplama alanları boş bırakılırsa tüm tablo tek grup sayılır. Aggregation ifadeleri boş bırakılırsa yalnızca benzersiz gruplama değerleri döner (DISTINCT etkisi).

Desteklenen aggregation fonksiyonları:

Count(field) CountDistinct(field) Min(field) Max(field) Sum(field) Avg(field)
Q12 — Her major için min/max mezuniyet yılı
Q12 = groupby(STUDENT, {MajorId}, {Min(GradYear), Max(GradYear)})
MajorIdMinOfGradYearMaxOfGradYear
1020042005
2020012005
3020032004
Q13 — Her major+mezuniyet yılı kombinasyonu için öğrenci sayısı
Q13 = groupby(STUDENT, {MajorId, GradYear}, {Count(SId)})
MajorIdGradYearCountOfSId
1020042
1020051
2020012
2020041
2020051
3020031
3020041
Q14, Q15 — Boş gruplama / boş aggregation
Q14 = groupby(STUDENT, {}, {Min(GradYear)})
→ Tüm öğrenciler tek grup → en erken mezuniyet yılı (tek satır, tek sütun döner)

Q15 = groupby(STUDENT, {MajorId}, {})
→ MajorId değerleri, yinelemeler kaldırılmış (DISTINCT MajorId)
Q16, Q17 — Count vs CountDistinct
Q16 = groupby(STUDENT, {}, {Count(MajorId)})
→ Majoru olan öğrenci sayısı → 9

Q17 = groupby(STUDENT, {}, {CountDistinct(MajorId)})
→ Farklı major sayısı → 3 (10, 20, 30)

Karmaşık örnek: Herhangi bir section'da verilen en yüksek A sayısı

Q18 – Q20
Q18 = select(ENROLL, Grade='A')
→ Sadece A alan kayıtlar

Q19 = groupby(Q18, {SectionId}, {Count(EId)})
→ Her section'da kaç A var? (SectionId 13→1, SectionId 53→2)

Q20 = groupby(Q19, {}, {Max(CountOfEId)})
→ En yüksek A sayısı → 2

Alternatif: tek iç içe ifade olarak (Q21) — aşağıdaki sorgu ağacıyla özetlenir:

GroupBy: {}, {Max(CountOfEId)}
GroupBy: {SectionId}, {Count(EId)}
Select: Grade = 'A'
ENROLL
09

İki-Tablo Operatörlerine Giriş

İki-tablo operatörleri iki girdi tablosunu alır ve tek bir çıktı tablosu üretir. Her birinin farklı bir semantiği vardır.

✖️
Product (Kartezyen Çarpım)
İki tablonun tüm olası satır kombinasyonlarını üretir.
🔗
Join (Birleştirme)
Product + Select: Koşulu sağlayan kombinasyonları döndürür.
🔍
Semijoin (Yarı Birleştirme)
1. tablonun; 2. tabloda eşleşen satırlarını döndürür (2. tablodan sütun gelmez).
🚫
Antijoin (Karşı Birleştirme)
1. tablonun; 2. tabloda eşleşmeyen satırlarını döndürür.
Union (Birleşim)
İki tablonun tüm satırlarını birleştirir (aynı şema zorunludur).
🔓
Outer Join
Join çıktısına eşleşmeyen satırları da null değerlerle ekler.
10

Product & Join

Product (Kartezyen Çarpım)

product(T1, T2)

İki tablodaki her satırın her satırla eşleştirilmesiyle oluşan tüm kombinasyonları üretir. Eğer T1'de N satır, T2'de M satır varsa çıktıda N×M satır olur. STUDENT (9 satır) × DEPT (3 satır) = 27 satır.

Örnek
Q22 = product(STUDENT, DEPT)
→ 9 öğrenci × 3 bölüm = 27 satır (her öğrenci her bölümle eşlenir)
⚠️
Product tek başına anlamsız kombinasyonlar üretir (ör. Compsci öğrencisi Drama bölümüyle eşleşir). Anlamlı sonuç için hemen ardından bir Select uygulanmalıdır — bu da bizi Join'e götürür.

Join (Birleştirme)

join(T1, T2, predicate)  ≡  select(product(T1, T2), predicate)

Join, Product ve Select'in bileşimidir. İki tablonun sadece belirtilen koşulu sağlayan satır kombinasyonlarını döndürür. Bu sayede gereksiz kombinasyonlar otomatik elenir.

Örnek — Q23 ve Q24 eşdeğerdir
Q23 = select(product(STUDENT, DEPT), MajorId=DId)
Q24 = join(STUDENT, DEPT, MajorId=DId)
→ Her öğrencinin kendi bölüm bilgisiyle eşleştirilmesi

Q23 için sorgu ağacı:

Select: MajorId = DId
Product
STUDENT
DEPT
11

Semijoin (Yarı Birleştirme)

Tanım

semijoin(T1, T2, predicate)

Semijoin, birinci tablonun satırlarından yalnızca ikinci tablodaki herhangi bir satırla koşulu sağlayanları döndürür. İkinci tablodan hiçbir sütun gelmez — yalnızca birinci tablonun sütunları çıktıdadır.

📌 Ne zaman kullanılır? "En az bir X'e sahip olan Y'leri bul" türündeki sorgularda kullanılır.
Örnek 1 — En az bir öğrencisi olan bölümler
Q35 = semijoin(DEPT, STUDENT, DId=MajorId)
→ Sonuç: DId ve DName sütunları, 3 satır (hepsi öğrencili)
Örnek 2 — Einstein'ın dersini almış öğrenciler
Q38 = select(SECTION, Prof='einstein')
→ Einstein'ın verdiği section'lar (SectId=43)

Q39 = semijoin(ENROLL, Q38, SectionId=SectId)
→ Einstein'ın section'larında kaydı olan enroll satırları

Q40 = semijoin(STUDENT, Q39, SId=StudentId)
→ Bu öğrencilerin tam kayıtları (joe ve amy)
Q38 çıktısı — SectIdCourseIdProfYearOffered
4332einstein2001
Q39 çıktısı — EIdStudentIdSectionIdGrade
24143C
34243B+
Q40 çıktısı — SIdSNameGradYearMajorId
1joe200410
2amy200420

Sorgu ağacı (Q38–Q40):

Semijoin: SId = StudentId
STUDENT
Semijoin: SectionId = SectId
ENROLL
Select: Prof = 'einstein'
SECTION
12

Antijoin (Karşı Birleştirme)

Tanım

antijoin(T1, T2, predicate)

Antijoin, Semijoin'in tam tersidir. Birinci tablonun satırlarından ikinci tablodaki herhangi bir satırla koşulu sağlamayanları döndürür. Yine yalnızca birinci tablonun sütunları çıktıdadır.

📌 Ne zaman kullanılır? "Hiç X'i olmayan Y'leri bul" veya "tüm X'leri olan Y'leri bul" türündeki sorgularda kullanılır.
Örnek 1 — Hiç öğrencisi olmayan bölümler
Q41 = antijoin(DEPT, STUDENT, DId=MajorId)
→ Hiç öğrencisi olmayan bölümler (verilen veride 0 sonuç)
Örnek 2 — F notu verilmeyen section'lar
Q42 = select(ENROLL, Grade='F')
Q43 = antijoin(SECTION, Q42, SectionId=SectId)
→ F notu verilen section'ların hariç tutulması

Gelişmiş örnek: Hiç F vermemiş profesörler

Q44–Q48
Q44 = select(ENROLL, Grade='F')
→ F alan kayıtlar

Q45 = semijoin(SECTION, Q44, SectionId=SectId)
→ F veren section'ların bilgileri (Prof sütunu dahil)

Q46 = rename(Q45, Prof, BadProf)
→ Prof sütunu BadProf olarak yeniden adlandırılır

Q47 = antijoin(SECTION, Q46, Prof=BadProf)
→ "Kötü" prof'larla eşleşmeyen section'lar (iyi prof'ların section'ları)

Q48 = groupby(Q47, {Prof}, {})
→ Benzersiz iyi profesör listesi
💡
Neden rename gerekli? Antijoin koşulunda iki farklı tablodaki aynı isimli alanı ayırt etmek için rename kullanılır. Q46'da Prof → BadProf yapılmasaydı antijoin(SECTION, Q46, Prof=Prof) hangi tablonun Prof'u olduğu belirsiz kalırdı.

Ek örnek: Her verdiği section'da F veren profesörler

Q43 → Q49–Q51 (Q42 = select(ENROLL, Grade='F') varsayılmıştır)
Q49 = rename(Q43, Prof, BadProf) -- Q43: F olmayan section'lar
Q50 = antijoin(SECTION, Q49, Prof=BadProf)
→ "Temiz" section'lara sahip prof'ların section'larını dışarıda bırak
Q51 = groupby(Q50, {Prof}, {})
→ Her section'unda F olan prof'lar
13

Union (Birleşim)

Tanım

union(T1, T2)

Union operatörü, iki tablonun tüm satırlarını birleştirir. Her iki tablonun da aynı şemaya (aynı sütun adları ve türleri) sahip olması zorunludur. SQL'deki UNION ile doğrudan örtüşür.

Örnek — Öğrenci ve profesör isimlerinin birleşimi
Q52 = rename(project(STUDENT, {SName}), SName, Person)
→ Öğrenci isimleri, "Person" sütunuyla

Q53 = rename(project(SECTION, {Prof}), Prof, Person)
→ Profesör isimleri, "Person" sütunuyla

Q54 = union(Q52, Q53)
→ Tüm öğrenci ve profesör isimlerinin birleşimi
⚠️
Union öncesinde sütun adlarını uyumlu hale getirmek için rename kullanılması gerekir. Q52'de SName → Person, Q53'te Prof → Person yapılmadan union mümkün olmazdı.
14

Outer Join (Dış Birleştirme)

Tanım

outerjoin(T1, T2, predicate)

Outer Join, normal join'in çıktısına ek olarak koşulu sağlamayan satırları da null değerlerle doldurarak ekler. Bu sayede hiç eşleşmesi olmayan satırlar da kaybolmaz.

Örnek — Her öğrencinin kayıtları (kaydı olmayanlar da dahil)
Q55 = outerjoin(STUDENT, ENROLL, SId=StudentId)
→ Kayıt olan öğrenciler: gerçek değerler
→ Kayıt olmayan öğrenciler (max, bob, art, pat, lee): null değerler
SIdSNameMajorIdGradYearEIdStudentIdSectionIdGrade
1joe10200414113A
1joe10200424143C
2amy20200434243B+
4sue20200544433B
4sue20200554453A
6kim20200164653A
3max102005nullnullnullnull
5bob302003nullnullnullnull
7art302004nullnullnullnull
8pat202001nullnullnullnull
9lee102004nullnullnullnull
💡
Outer Join türleri (SQL'de):
LEFT OUTER JOIN: Sol tablonun tüm satırları korunur (yukarıdaki örnek)
RIGHT OUTER JOIN: Sağ tablonun tüm satırları korunur
FULL OUTER JOIN: Her iki tablonun tüm satırları korunur
15

SQL (Structured Query Language)

SQL, relational algebra üzerine inşa edilmiş endüstri standardı sorgu dilidir. Her SQL sorgusu bir relational algebra sorgusuna karşılık gelir. SQL komutları 5 ana kategoriye ayrılır:

DDL
Data Definition Language
CREATE
ALTER
DROP
TRUNCATE
DML
Data Manipulation Language
INSERT
UPDATE
DELETE
DCL
Data Control Language
GRANT
REVOKE
DQL
Data Query Language
SELECT
(FROM, WHERE,
GROUP BY...)
TCL
Transactional Control Language
COMMIT
ROLLBACK
SAVEPOINT

DDL — Data Definition Language

Veritabanı şemasını tanımlamak ve yapısal değişiklikler yapmak için kullanılır.

  • CREATE: Yeni veritabanı veya tablo oluşturur
  • ALTER: Mevcut tabloya sütun ekler/değiştirir/siler
  • DROP: Tablo veya veritabanını siler (geri alınamaz)
  • TRUNCATE: Tablodaki tüm satırları siler (yapı kalır)
DML — Data Manipulation Language

Veritabanındaki veriyi güncellemek için 4 temel yol:

  • Değerleri vererek tek satır eklemek (INSERT INTO ... VALUES)
  • Bir sorgu sonucunu kullanarak çoklu satır eklemek (INSERT INTO ... SELECT)
  • Satırları silmek (DELETE FROM ... WHERE)
  • Mevcut satırları güncellemek (UPDATE ... SET ... WHERE)
DCL — Data Control Language

Kullanıcı yetki yönetimi için:

  • GRANT: Belirtilen kullanıcıya belirtilen işlemleri yapma izni verir
  • REVOKE: Kullanıcının veritabanı nesnelerine erişimini kaldırır
DQL — Data Query Language: SQL Sorgu Yapısı

Bir SQL sorgusunun 6 temel cümlesi (clause) vardır:

SQL Sorgu Şablonu
SELECT sütunlar veya ifadeler
FROM tablo(lar) veya subquery
WHERE koşul
GROUP BY gruplama alanları
HAVING aggregation koşulu
ORDER BY sıralama alanları
16

Uçuş Veritabanı — Örnek Sorgular

passenger (pid, pname, pgender, pcity)
agency (aid, aname, acity)
flight (fid, fdate, time, src, dest)
booking (pid, aid, fid, fdate)
a
New Delhi'ye yapılan tüm uçuşların tam bilgilerini getir.
select(flight, dest = "New Delhi")
Tek bir tablo üzerinde koşul — Select operatörü yeterli.
b
Chennai'den New Delhi'ye olan uçuşların bilgilerini getir.
select(flight, src = "Chennai" AND dest = "New Delhi")
Hem kaynak hem hedef koşulu — AND ile birleştirilir.
c
Pid=123 olan yolcunun, 06/11/2020'den önce Chennai'ye yapılan uçuş numaralarını bul.
T1 = select(flight, dest = "Chennai" AND fdate < 06/11/2020)
→ Koşulları sağlayan uçuşlar

T2 = join(T1, booking, T1.fid = booking.fid)
→ Bu uçuşların rezervasyonlarıyla birleştir

T3 = select(T2, pid = 123)
→ Sadece pid=123'ün rezervasyonları

T4 = project(T3, {fid})
→ Yalnızca uçuş numaraları
d
En az bir uçuşta rezervasyonu olan yolcuların isimlerini bul.
T1 = semijoin(passenger, booking, passenger.pid = booking.pid)
→ Rezervasyonu olan yolcular (sadece passenger sütunları)

T2 = project(T1, {pname})
→ Sadece isimler
Semijoin: "en az bir ... sahip olanlar" için idealdir.
e
Hiçbir uçuşta rezervasyonu olmayan yolcuların isimlerini bul.
T1 = antijoin(passenger, booking, passenger.pid = booking.pid)
→ Rezervasyonu olmayan yolcular

T2 = project(T1, {pname})
→ Sadece isimler
Antijoin: "hiç ... olmayanlar" için idealdir (semijoin'in tersi).
f
Yolcu id=123 ile aynı şehirde bulunan acentelerin isimlerini bul.
T1 = select(passenger, pid = 123)
→ Yolcu 123'ün kaydı (şehir bilgisi dahil)

T2 = join(T1, agency, pcity = acity)
→ Aynı şehirdeki acentelerle birleştir

T3 = project(T2, {aname})
→ Sadece acente isimleri
h
01/12/2020 veya 02/12/2020 tarihlerinde 16:00'da planlanmış uçuşları getir.
select(flight, fdate = 01/12/2020 AND time = 16:00)
select(flight, fdate = 02/12/2020 AND time = 16:00)
Union: "ya X ya Y veya her ikisi" — iki ayrı select'in birleşimi. Her iki select de aynı şemayı (flight tablosu) döndürdüğünden union uygulanabilir.
j
Jet Acentesiyle ilişkili tüm erkek yolcuların bilgilerini bul.
T1 = join(passenger, booking, passenger.pid = booking.pid)
→ Yolcular + rezervasyonları

T2 = join(T1, agency, agency.aid = booking.aid)
→ Acente bilgisi eklendi

T3 = project(select(T2, pgender="Male" AND aname="Jet agency"), {pid, pname})
→ Erkek ve Jet müşterisi olup isim ve id'si
k
2023'te en fazla rezervasyon yapan müşteri(ler)i bul.
T1 = select(booking, fdate = 2023)
→ 2023 rezervasyonları

T2 = groupby(T1, {pid}, {max(count(pid))})
→ Her müşterinin rezervasyon sayısı; max olanlar

T3 = join(passenger, T2, passenger.pid = T2.pid)
→ Yolcu bilgileriyle birleştir
Karmaşık bir sorgu: önce filtre (select), sonra gruplama ve aggregation (groupby), son olarak join ile tam bilgi.
17

Genel Özet

Select
Satır filtreler. Sütunlar değişmez.
Project
Sütun seçer. Satırlar değişmez.
Sort
Satırları sıralar. Hiçbir şey değişmez, sadece sıra.
Rename
Bir sütun adını değiştirir.
Extend
Hesaplanmış yeni sütun ekler.
GroupBy
Gruplama + aggregation. Her grup → 1 satır.
Product
Kartezyen çarpım. N×M satır üretir.
Join
Product + Select. Koşullu birleştirme.
Semijoin
T1'in eşleşen satırları. T2 sütunu gelmez.
Antijoin
T1'in eşleşmeyen satırları. T2 sütunu gelmez.
Union
İki tablonun satırlarını birleştirir (aynı şema gerekli).
Outer Join
Join + eşleşmeyenleri null ile ekler.