İçeriğe geç
XDD.baby Yazılım Öğrenme
Sözlük Hata rehberi 0/56

Blok 5 · Ders 34

SELECT: veriyi sorgulamak

SELECT, WHERE, ORDER BY, LIMIT, LIKE, NULL, toplama fonksiyonları ve GROUP BY ile maç istatistiklerini sorgulamak.

Bu dersin sonunda

  • SELECT ile bir tablodan istediğin sütunları alabilir, *'ın neden sakıncalı olduğunu açıklayabilirsin
  • WHERE içinde AND, OR, NOT, IN ve BETWEEN kullanarak satırları süzebilirsin
  • Sonucu ORDER BY ile birden çok sütuna göre sıralayıp LIMIT ve OFFSET ile sayfalara bölebilirsin
  • LIKE ve ILIKE ile metin arayabilir, boş değerleri IS NULL ile doğru bulabilirsin
  • COUNT, SUM, AVG, MIN, MAX, GROUP BY ve HAVING ile oyuncu ve şampiyon istatistiği çıkarabilirsin
  • Bir sorgunun hangi sırayla çalıştığını bilerek SQL hata mesajlarını okuyup düzeltebilirsin
Bu sayfada
  1. Örnek veri: maç geçmişi tablosu
  2. SELECT: hangi sütunlar gelsin?
  3. WHERE: hangi satırlar gelsin?
  4. Koşulları birleştirmek: AND, OR, NOT
  5. IN ve BETWEEN: kısa yollar
  6. ORDER BY: sıralamak
  7. LIMIT ve OFFSET: ilk birkaç satır ve sayfalar
  8. SELECT'te hesap ve takma ad
  9. LIKE ve ILIKE: metin içinde aramak
  10. NULL: bilinmeyen değer
  11. Toplama fonksiyonları: COUNT, SUM, AVG, MIN, MAX
  12. GROUP BY: her oyuncu için ayrı hesap
  13. HAVING: grupları süzmek
  14. Sorgu hangi sırayla çalışır?
  15. Sorgularını bir dosyada sakla
  16. Sık hatalar
  17. Görevler
  18. Kendini sına
  19. Kayıt noktası

Ders 33'te veriyi bir tabloya koydun. Veritabanının asıl gücü ise o veriye soru sorabilmekte: "Hangi şampiyonla en çok kazandım?", "Deniz maç başına kaç kill alıyor?", "En az iki kez oynanan şampiyonlardan hangisi daha iyi gidiyor?" Bu soruların hepsi tek bir SQL komutuyla sorulur: SELECT. Bu derste onu bir maç geçmişi tablosu üzerinde adım adım kuracaksın: sütun seçmek, satır süzmek, sıralamak, saymak ve gruplamak. Sonunda istatistik sitelerinin gösterdiği tabloları kendi sorgunla üretebileceksin.

SELECT yalnız okur, tabloyu değiştirmez. İstediğin kadar dene; yanlış yazılmış bir sorgu verini bozamaz.

Örnek veri: maç geçmişi tablosu

Dersin bütün sorguları aynı tabloda çalışacak: üç arkadaşın (Deniz, Ece ve Mert) son 20 maçı. Her satır bir maç, her sütun o maçın bir bilgisi. Sütunlar ve tipleri:

  • id (integer): maçın numarası; veritabanı kendisi verir (birincil anahtar).
  • oyuncu (text): maçı oynayan, Deniz, Ece ya da Mert.
  • sampiyon (text): seçilen şampiyon.
  • rol (text): üst, orman, orta, alt ya da destek; ARAM maçlarında boş.
  • sonuc (text): zafer ya da yenilgi.
  • kill, olum, asist (integer): maç sonu skoru, 8/2/11 gibi.
  • sure_dk (integer): maçın süresi, dakika olarak.
  • tarih (timestamptz): maçın başladığı an.

Aşağıdaki betik tabloyu kurar ve 20 maçı ekler. Bu dersin amacı veri girmek değil sorgu yazmak; bu yüzden betiği kopyalayabilirsin. Neon'da SQL Editor'ü aç (Ders 33), New Query ile boş bir sayfa aç, betiği yapıştır ve Run'a bas. Dersin sonunda bu betiği maclar.sql adıyla bir dosyaya da kaydedeceksin.

maclar.sql
-- Maç geçmişi tablosu. Yeniden çalıştırırsan tabloyu sıfırdan kurar.DROP TABLE IF EXISTS maclar; CREATE TABLE maclar (    id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    oyuncu text,    sampiyon text,    rol text,    sonuc text,    kill integer,    olum integer,    asist integer,    sure_dk integer,    tarih timestamptz); INSERT INTO maclar (oyuncu, sampiyon, rol, sonuc, kill, olum, asist, sure_dk, tarih) VALUES    ('Deniz', 'Ahri',    'orta',   'zafer',   8,  2, 11, 31, '2026-09-28 20:40+03'),    ('Ece',   'Jinx',    'alt',    'zafer',   12, 3, 8,  32, '2026-09-28 21:30+03'),    ('Mert',  'Garen',   'üst',    'zafer',   9,  3, 5,  34, '2026-09-29 19:10+03'),    ('Deniz', 'Ahri',    'orta',   'yenilgi', 3,  6, 4,  27, '2026-09-29 22:05+03'),    ('Ece',   'Thresh',  'destek', 'zafer',   1,  4, 19, 36, '2026-09-30 18:20+03'),    ('Mert',  'Lee Sin', 'orman',  'yenilgi', 4,  7, 10, 29, '2026-09-30 21:45+03'),    ('Deniz', 'Lux',     'destek', 'zafer',   2,  3, 15, 35, '2026-10-01 20:15+03'),    ('Ece',   'Kai''Sa', 'alt',    'yenilgi', 5,  7, 4,  28, '2026-10-01 22:40+03'),    ('Deniz', 'Jinx',    NULL,     'zafer',   14, 6, 22, 19, '2026-10-02 19:55+03'),    ('Mert',  'Darius',  'üst',    'yenilgi', 3,  8, 3,  26, '2026-10-02 22:20+03'),    ('Ece',   'Jinx',    'alt',    'zafer',   9,  2, 10, 30, '2026-10-03 17:25+03'),    ('Deniz', 'Yasuo',   'orta',   'yenilgi', 6,  9, 2,  24, '2026-10-03 20:50+03'),    ('Mert',  'Lee Sin', 'orman',  'zafer',   6,  4, 14, 31, '2026-10-04 16:30+03'),    ('Ece',   'Lux',     NULL,     'yenilgi', 6,  8, 25, 17, '2026-10-04 19:00+03'),    ('Deniz', 'Ahri',    'orta',   'zafer',   11, 1, 7,  29, '2026-10-05 21:20+03'),    ('Ece',   'Ashe',    'alt',    'zafer',   7,  0, 12, 25, '2026-10-06 20:05+03'),    ('Mert',  'Teemo',   'üst',    'yenilgi', 2,  7, 6,  35, '2026-10-07 22:30+03'),    ('Deniz', 'Annie',   'orta',   'zafer',   7,  4, 9,  33, '2026-10-08 21:00+03'),    ('Ece',   'Jinx',    'alt',    'yenilgi', 3,  5, 6,  40, '2026-10-09 23:15+03'),    ('Mert',  'Garen',   NULL,     'zafer',   10, 9, 18, 21, '2026-10-10 18:45+03');

Betikte dört şeye dikkat et:

  • -- ile başlayan satır yorumdur. JavaScript'teki // gibi; veritabanı onu okumaz.
  • DROP TABLE IF EXISTS maclar; "maclar adında bir tablo varsa sil" demek. Betiği ikinci kez çalıştırırsan tablo baştan kurulur ve maçlar iki kez eklenmez. İlk çalıştırmada NOTICE: table "maclar" does not exist, skipping gibi bir bilgi satırı görebilirsin; hata değil. DROP TABLE geri alınamaz (Ders 33), ama bu tabloda yalnız deneme verisi var.
  • Üç maçta rol yerine tırnaksız NULL yazıyor. Bunlar ARAM maçları: orada koridor seçilmez, rol bilgisi yok. NULL'u dersin ortasında ayrıntılı göreceksin.
  • 'Kai''Sa' Ders 33'teki gibi yazıldı: metnin içindeki kesme işareti iki kez yazılır. Saatler de Ders 33'teki gibi Türkiye saatiyle (+03) girildi; Neon onları UTC'ye çevirip gösterir.

SELECT: hangi sütunlar gelsin?

İlk sorgu, Ders 33'te tabloya bakmak için kullandığın kalıp: tablonun tamamını getirir. Editöre kendin yaz ve Run'a bas; altta şu sonuç tablosu belirir:

SQL
SELECT * FROM maclar;
idoyuncusampiyonrolsonuckillolumasistsure_dktarih
1DenizAhriortazafer8211312026-09-28 17:40:00+00
2EceJinxaltzafer1238322026-09-28 18:30:00+00
3MertGarenüstzafer935342026-09-29 16:10:00+00
4DenizAhriortayenilgi364272026-09-29 19:05:00+00
5EceThreshdestekzafer1419362026-09-30 15:20:00+00
6MertLee Sinormanyenilgi4710292026-09-30 18:45:00+00
7DenizLuxdestekzafer2315352026-10-01 17:15:00+00
8EceKai'Saaltyenilgi574282026-10-01 19:40:00+00
9DenizJinxNULLzafer14622192026-10-02 16:55:00+00
10MertDariusüstyenilgi383262026-10-02 19:20:00+00
11EceJinxaltzafer9210302026-10-03 14:25:00+00
12DenizYasuoortayenilgi692242026-10-03 17:50:00+00
13MertLee Sinormanzafer6414312026-10-04 13:30:00+00
14EceLuxNULLyenilgi6825172026-10-04 16:00:00+00
15DenizAhriortazafer1117292026-10-05 18:20:00+00
16EceAshealtzafer7012252026-10-06 17:05:00+00
17MertTeemoüstyenilgi276352026-10-07 19:30:00+00
18DenizAnnieortazafer749332026-10-08 18:00:00+00
19EceJinxaltyenilgi356402026-10-09 20:15:00+00
20MertGarenNULLzafer10918212026-10-10 15:45:00+00

Sorgunun dört parçası var:

  • SELECT: "seç, getir". Arkasına hangi sütunları istediğini yazarsın.
  • * (yıldız): "bütün sütunlar".
  • FROM maclar: "maclar tablosundan".
  • ; komutun bittiğini söyler. Editörde tek komut varken unutsan da çalışır, ama komutları birbirinden ayıran odur; her sorgunun sonuna koymayı alışkanlık edin.

NULL yazan hücreler boş hücreler; başka araçlar onları boş da gösterebilir. Tarihler Ders 33'te gördüğün gibi UTC'ye çevrilmiş gelir (+00): 20:40+03 diye girilen maç 17:40:00+00 görünür, ikisi aynı an.

Anahtar sözcükleri büyük harfle yazmak bir gelenek: SQL, anahtar sözcüklerde ve tırnaksız adlarda büyük-küçük harfe bakmaz; select * from maclar; da çalışır. Büyük yazınca hangi sözcüğün SQL'e, hangisinin senin tablona ait olduğu bir bakışta ayrılır.

Çoğu zaman bütün sütunlar gerekmez. İstediğin sütunları virgülle ayırarak yaz; sonuç o sütunlarla, senin yazdığın sırayla gelir:

SQL
SELECT oyuncu, sampiyon, sonuc FROM maclar;
oyuncusampiyonsonuc
DenizAhrizafer
EceJinxzafer
MertGarenzafer
DenizAhriyenilgi
EceThreshzafer
MertLee Sinyenilgi
DenizLuxzafer
EceKai'Sayenilgi
DenizJinxzafer
MertDariusyenilgi
EceJinxzafer
DenizYasuoyenilgi
MertLee Sinzafer
EceLuxyenilgi
DenizAhrizafer
EceAshezafer
MertTeemoyenilgi
DenizAnniezafer
EceJinxyenilgi
MertGarenzafer

Neden her zaman * yazmıyoruz?

* bir tabloyu ilk kez tanırken çok kullanışlıdır. Ama bir programın içinde her gün çalışacak sorgularda onu alışkanlık hâline getirme:

  • Gereksiz veri taşır. Ekranda yalnız şampiyon adı gösterecekken tarih, süre ve skor da gelir. 20 satırda fark etmez; iki milyon satırda veritabanı, ağ ve sunucun boşuna yorulur.
  • Tablo değişince sürpriz yapar. Tabloya yeni bir sütun eklenince (Ders 35'te ALTER TABLE ile yapacaksın) * onu da getirmeye başlar ve kodun beklemediği bir alanla karşılaşır.
  • Gizli bilgiyi sızdırabilir. Ders 31'deki gibi bir kullanicilar tablosunda sifre_hash sütunu varsa ve API'n SELECT * sonucunu olduğu gibi JSON'a çevirip gönderiyorsa, hash'ler herkesin tarayıcısına gider.
  • Okuyanı yorar. SELECT oyuncu, sampiyon ne istediğini kendisi söyler; SELECT * okuyanı tabloya bakmaya zorlar.
Sık hatasyntax error at or near "FROM"

Ne zaman çıkar? Sütun listesinin sonunda virgül bırakınca. SQL virgülden sonra bir sütun daha bekler, karşısına FROM çıkar.

SQL
SELECT oyuncu, sampiyon, FROM maclar;
Hata çıktısı
ERROR:  syntax error at or near "FROM"

Nasıl düzelir? Son sütundan sonra virgül koyma. syntax error at or near "X" mesajı "X'in yakınında dilbilgisi hatası" demek; sorun ya X'in kendisinde ya da hemen önündedir. Hataları Ders 33'teki gibi psql biçiminde gösteriyoruz; Neon'da biçim biraz farklı görünebilir, ama asıl cümle aynıdır.

WHERE: hangi satırlar gelsin?

SELECT sütunları seçer, ama satırların hepsi yine gelir. Yalnız Ahri maçlarını görmek istiyorsan satırları süzmen gerekir. Bu iş WHERE'in:

SQL
SELECT sampiyon, sonuc, kill FROM maclar WHERE sampiyon = 'Ahri';
sampiyonsonuckill
Ahrizafer8
Ahriyenilgi3
Ahrizafer11

WHERE'den sonra bir koşul gelir. PostgreSQL tablonun her satırı için koşula bakar: doğruysa satır sonuca girer, değilse elenir. Ders 11'de öğrendiğin filter ile aynı fikir, maclar.filter((mac) => mac.sampiyon === "Ahri") gibi; tek fark, işi bu kez veritabanının yapması.

SELECT sampiyon, killsütunları seçer (mor çerçeve) id oyuncu sampiyon sonuc kill 1 Deniz Ahri zafer 8 2 Ece Jinx zafer 12 3 Mert Garen zafer 9 4 Deniz Ahri yenilgi 3 … 10 satır daha 15 Deniz Ahri zafer 11 … 5 satır daha Sonuç: 3 satır sampiyon kill Ahri 8 Ahri 3 Ahri 11 WHERE sampiyon = 'Ahri'satırları seçer (yeşil şerit)
Şekil 34.1 SELECT dikey bir dilim alır (sütunlar), WHERE yatay bir dilim (satırlar). Sonuç, ikisinin kesiştiği hücrelerdir.

Koşullarda kullanacağın karşılaştırma işaretleri:

İşaretAnlamıÖrnek
=eşitsonuc = 'zafer'
<> ya da !=eşit değilrol <> 'orta'
<, >küçük, büyükkill > 10
<=, >=küçük ya da eşit, büyük ya da eşitsure_dk >= 30

SQL'de eşitlik tek = ile sorulur. JavaScript'te = "koy", === "eşit mi" demekti; SQL'de WHERE içindeki = her zaman karşılaştırmadır. Sayılar tırnaksız yazılır. 10 ya da daha fazla kill alınan maçlar:

SQL
SELECT oyuncu, sampiyon, kill FROM maclar WHERE kill >= 10;
oyuncusampiyonkill
EceJinx12
DenizJinx14
DenizAhri11
MertGaren10

Metin tek tırnakla yazılır

Ders 33'teki kural WHERE'de de geçerli: metin değerleri tek tırnak içinde yazılır, 'Ahri'. JavaScript'te hep çift tırnak kullandığın için parmakların koşul yazarken de ona gidecek:

Sık hatacolumn "Ahri" does not exist

Ne zaman çıkar? WHERE koşulunda metni çift tırnakla yazınca. SQL'de çift tırnak bir ad (sütun ya da tablo adı) demektir; PostgreSQL Ahri adında bir sütun arar, bulamaz.

SQL
SELECT sampiyon, sonuc, kill FROM maclar WHERE sampiyon = "Ahri";
Hata çıktısı
ERROR:  column "Ahri" does not exist

Nasıl düzelir? Metni tek tırnakla yaz: WHERE sampiyon = 'Ahri'. Tek tırnak değer, çift tırnak ad.

Metin karşılaştırması büyük-küçük harfe duyarlıdır: WHERE sampiyon = 'ahri' hiç satır döndürmez, çünkü tablodaki değer büyük A ile Ahri. Harf büyüklüğüne bakmayan aramayı LIKE bölümünde göreceksin.

Koşulları birleştirmek: AND, OR, NOT

Gerçek sorular çoğu zaman tek koşulla bitmez: "Deniz'in kazandığı maçlar" iki koşul demek. Koşullar eklendikçe sorgu da uzar ve tek satıra sığmaz olur.

Sorgunun SELECT …, FROM …, WHERE … gibi bir anahtar sözcükle başlayan her parçasına cümlecik (clause) denir. SQL satır sonlarına ve girintiye bakmaz, komutu yalnız ; bitirir. Bu yüzden bundan sonra uzun sorguları cümleciklerine bölüp alt alta yazacağız; editörde de böyle yaz, okuması kolaylaşır.

AND iki koşulun ikisi de doğruysa satırı geçirir:

SQL
SELECT oyuncu, sampiyon, sonuc, killFROM maclarWHERE oyuncu = 'Deniz' AND sonuc = 'zafer';
oyuncusampiyonsonuckill
DenizAhrizafer8
DenizLuxzafer2
DenizJinxzafer14
DenizAhrizafer11
DenizAnniezafer7

OR için koşullardan birinin doğru olması yeter. Ahri ya da Yasuo oynanan maçlar:

SQL
SELECT oyuncu, sampiyon, sonucFROM maclarWHERE sampiyon = 'Ahri' OR sampiyon = 'Yasuo';
oyuncusampiyonsonuc
DenizAhrizafer
DenizAhriyenilgi
DenizYasuoyenilgi
DenizAhrizafer

NOT bir koşulu tersine çevirir. Mert'in kazanamadığı maçlar:

SQL
SELECT oyuncu, sampiyon, sonucFROM maclarWHERE oyuncu = 'Mert' AND NOT sonuc = 'zafer';
oyuncusampiyonsonuc
MertLee Sinyenilgi
MertDariusyenilgi
MertTeemoyenilgi

Bunlar Ders 3'teki mantık işaretlerinin (&&, ||, !) SQL'deki adları. Kuralları da aynı, tuzakları da.

AND, OR'dan önce bağlanır

Ahri ya da Lux oynanan maçlardan kazanılanları istiyorsun ve aklındaki cümleyi sırasıyla yazıyorsun:

SQL
SELECT oyuncu, sampiyon, sonucFROM maclarWHERE sampiyon = 'Ahri' OR sampiyon = 'Lux' AND sonuc = 'zafer';
oyuncusampiyonsonuc
DenizAhrizafer
DenizAhriyenilgi
DenizLuxzafer
DenizAhrizafer

Listede bir yenilgi var. Hata mesajı yok, sorgu çalıştı, ama sonuç yanlış. Sebebi Ders 3'tekiyle aynı: AND, OR'dan önce bağlanır. PostgreSQL bu koşulu "Ahri ya da (Lux ve zafer)" diye okudu ve Ahri'nin bütün maçları geçti. Ne kastettiğini parantezle söyle:

SQL
SELECT oyuncu, sampiyon, sonucFROM maclarWHERE (sampiyon = 'Ahri' OR sampiyon = 'Lux') AND sonuc = 'zafer';
oyuncusampiyonsonuc
DenizAhrizafer
DenizLuxzafer
DenizAhrizafer

IN ve BETWEEN: kısa yollar

Aynı sütun için uzun bir OR zinciri yazmak yerine IN ile bir liste verebilirsin. Yukarıdaki parantezli sorgu kısaca şöyle yazılır:

SQL
SELECT oyuncu, sampiyon, sonucFROM maclarWHERE sampiyon IN ('Ahri', 'Lux') AND sonuc = 'zafer';
oyuncusampiyonsonuc
DenizAhrizafer
DenizLuxzafer
DenizAhrizafer

IN "bu listedekilerden biri" demek; Ders 5'teki takim.includes("Lux") kalıbının SQL'deki karşılığı. NOT IN ise "listedekilerin hiçbiri".

BETWEEN bir aralık verir ve iki ucu da dahildir. 6 ile 9 arasında kill alınan maçlar:

SQL
SELECT oyuncu, sampiyon, kill FROM maclar WHERE kill BETWEEN 6 AND 9;
oyuncusampiyonkill
DenizAhri8
MertGaren9
EceJinx9
DenizYasuo6
MertLee Sin6
EceLux6
EceAshe7
DenizAnnie7

Sonuçta tam 6 ve tam 9 kill'li maçlar da var. Aynı koşulu uzun yoldan kill >= 6 AND kill <= 9 diye de yazabilirdin.

ORDER BY: sıralamak

Şimdiye kadarki sonuçlar tabloya eklendikleri sırayla geldi. Bu bir tesadüf: ORDER BY yazmadığın sürece PostgreSQL sıra sözü vermez. Tablo büyüyüp değiştikçe aynı sorgu satırları başka bir sırayla getirebilir. Sıra önemliyse, ki neredeyse her zaman önemlidir, ORDER BY yaz. Deniz'in maçları, en çok kill alınan en üstte:

SQL
SELECT sampiyon, sonuc, killFROM maclarWHERE oyuncu = 'Deniz'ORDER BY kill DESC;
sampiyonsonuckill
Jinxzafer14
Ahrizafer11
Ahrizafer8
Anniezafer7
Yasuoyenilgi6
Ahriyenilgi3
Luxzafer2

DESC (descending, azalan) büyükten küçüğe demek. Hiçbir şey yazmazsan ya da ASC (ascending, artan) yazarsan küçükten büyüğe sıralar. Metinler alfabetik, tarihler eskiden yeniye dizilir.

ORDER BY'ın yeri WHERE'den sonradır. Türkçe harfli metinlerin sırası veritabanının dil ayarına bağlıdır; bazı ayarlarda Çağla, Zeynep'ten sonra bile gelebilir.

Birden çok sütuna göre

Kazanılan maçlar; önce oyuncuya göre, aynı oyuncunun maçları da kill'e göre:

SQL
SELECT oyuncu, sampiyon, killFROM maclarWHERE sonuc = 'zafer'ORDER BY oyuncu, kill DESC;
oyuncusampiyonkill
DenizJinx14
DenizAhri11
DenizAhri8
DenizAnnie7
DenizLux2
EceJinx12
EceJinx9
EceAshe7
EceThresh1
MertGaren10
MertGaren9
MertLee Sin6

Her sütunun yönü ayrı yazılır: oyuncu'nun yanında DESC olmadığı için o artan (A'dan Z'ye) sıralandı, kill ise azalan.

LIMIT ve OFFSET: ilk birkaç satır ve sayfalar

Bütün listeye değil, en iyi üç maça bakmak istiyorsun. LIMIT sonucun ilk birkaç satırını alır:

SQL
SELECT oyuncu, sampiyon, kill FROM maclar ORDER BY kill DESC LIMIT 3;
oyuncusampiyonkill
DenizJinx14
EceJinx12
DenizAhri11

LIMIT sorgunun en sonuna yazılır ve ORDER BY ile birlikte anlam kazanır. ORDER BY olmadan LIMIT 3 "herhangi üç satır" demektir; hangilerinin geleceği belli değildir.

OFFSET ile sayfa sayfa

Maç geçmişi ekranları bütün maçları bir anda yüklemez: önce birkaç maç gelir, "daha fazla göster"e basınca sonrakiler. Buna sayfalama denir.

OFFSET n "ilk n satırı atla" demek; LIMIT ile birlikte sonuçtan bir sayfa keser. Kill sıralamasını 5'er maçlık sayfalara böldüğünü düşün. 2. sayfa, ilk 5 satırı atlayıp sonraki 5'ini alır:

SQL
SELECT id, oyuncu, sampiyon, killFROM maclarORDER BY kill DESC, idLIMIT 5 OFFSET 5;
idoyuncusampiyonkill
11EceJinx9
1DenizAhri8
16EceAshe7
18DenizAnnie7
12DenizYasuo6

Üçüncü sayfa için OFFSET 10, dördüncü sayfa için OFFSET 15 yazarsın: her sayfada önceki sayfaların bütün satırları atlanır. Son sayfadan sonrasını istersen boş bir tablo gelir; bu hata değil.

SELECT'te hesap ve takma ad

SELECT'in arkasına yalnız sütun adı değil, hesap da yazabilirsin. Kill ile asistin toplamı, oyuncunun takım savaşlarına ne kadar katıldığını kabaca gösterir. Bu toplamın en yüksek olduğu üç maç:

SQL
SELECT oyuncu, sampiyon, kill + asistFROM maclarORDER BY kill + asist DESCLIMIT 3;
oyuncusampiyon?column?
DenizJinx36
EceLux31
MertGaren28

Hesap doğru, ama sütunun adı ?column? oldu. Ders 33'te söylenen buydu: hesaba ad vermezsen PostgreSQL ona kendisi bir ad uyduramaz.

Ders 33'teki ilk sorguda yaptığın gibi AS ile ad ver. Sonuç sütununa böyle verilen ada takma ad (alias) denir. Takma adı ORDER BY'da da kullanabilirsin:

SQL
SELECT oyuncu, sampiyon, kill + asist AS katkiFROM maclarORDER BY katki DESCLIMIT 3;
oyuncusampiyonkatki
DenizJinx36
EceLux31
MertGaren28

LIKE ve ILIKE: metin içinde aramak

Şampiyon seçim ekranındaki arama kutusunu düşün: a yazınca yalnız adında a geçenler kalır. = bunu yapamaz; satırı ancak metnin tamamı eşitse bulur. Metnin bir kısmına göre aramak için LIKE ve bir kalıp kullanırsın:

SQL
SELECT oyuncu, sampiyon FROM maclar WHERE sampiyon LIKE 'A%';
oyuncusampiyon
DenizAhri
DenizAhri
DenizAhri
EceAshe
DenizAnnie

Kalıptaki % "burada herhangi sayıda karakter olabilir, hiç olmayabilir de" demek; 'A%' A ile başlayan her şeyi tutar. Kalıplarda iki joker karakter var: % ve tam bir karakterin yerini tutan _ (alt çizgi). Bu tablodaki şampiyonlarla:

  • 'A%': A ile başlayan. Ahri, Ashe, Annie.
  • '%e': e ile biten. Ashe, Annie.
  • '%in%': içinde "in" geçen. Jinx, Lee Sin.
  • '____' (dört alt çizgi): tam dört karakterli. Ahri, Jinx, Ashe.
  • '_e%': ikinci harfi e olan. Lee Sin, Teemo.

Kalıpta joker karakter yoksa LIKE, = gibi davranır: sampiyon LIKE 'Lee' hiçbir şey bulmaz, çünkü tam ad Lee Sin.

Büyük-küçük harf: ILIKE

LIKE da = gibi büyük-küçük harfe duyarlıdır. Kullanıcı arama kutusuna küçük a yazarsa:

SQL
SELECT oyuncu, sampiyon FROM maclar WHERE sampiyon LIKE 'a%';
oyuncusampiyon

Boş tablo, çünkü bütün adlar büyük harfle başlıyor. PostgreSQL'in ILIKE'ı aynı kalıbı harf büyüklüğüne bakmadan uygular:

SQL
SELECT oyuncu, sampiyon FROM maclar WHERE sampiyon ILIKE 'a%';
oyuncusampiyon
DenizAhri
DenizAhri
DenizAhri
EceAshe
DenizAnnie

Arama kutuları neredeyse her zaman ILIKE ister. İki not: ILIKE PostgreSQL'e özgüdür, başka veritabanlarında adı ve davranışı farklıdır. Türkçedeki İ, ı gibi harflerde büyük-küçük eşleştirmesi veritabanının dil ayarına bağlıdır; Ders 9'daki toLocaleUpperCase("tr") tuzağının bir benzeri.

NULL: bilinmeyen değer

Üç ARAM maçında rol boş. Onları bulmak için aklına gelen ilk sorgu muhtemelen şudur:

Sık hataWHERE rol = NULL hiç satır döndürmüyor

Ne zaman çıkar? Boş (NULL) değerleri = ile aramaya kalkınca. Hata mesajı yok; sorgu çalışır ve boş bir tablo döner. Bu yüzden fark etmesi zordur.

SQL
SELECT id, oyuncu, sampiyon, rol FROM maclar WHERE rol = NULL;
idoyuncusampiyonrol

Sütun başlıkları geldi, ama tek satır yok: sorgu 0 satır döndürdü.

Nasıl düzelir? NULL'u IS NULL ile, NULL olmayanları IS NOT NULL ile ara. Aynı tuzak <> için de geçerli: rol <> 'orta' rolü boş maçları da dışarıda bırakır.

Neden boş? Ders 33'te NULL'u "değer yok" diye tanıdın. Karşılaştırmalarda PostgreSQL onu "bilinmiyor" diye ele alır. rol = NULL sorusu "bu maçın rolü, bilinmeyen bir şeye eşit mi?" olur; cevabı da "bilinmiyor"dur.

Bunu kendin görebilirsin. Ders 33'teki ilk sorgun gibi SELECT'i tablosuz çalıştır; tek satırlık bir hesap makinesi gibi davranır:

SQL
SELECT NULL = NULL AS bos_bosa_esit_mi,    'orta' = 'orta' AS orta_ortaya_esit_mi;
bos_bosa_esit_miorta_ortaya_esit_mi
NULLtrue

İki NULL'un eşitliği bile NULL çıktı, yani bilinmiyor. (Bazı araçlar true yerine t yazar.) WHERE ise yalnız sonucu doğru (true) olan satırları geçirir; yanlış çıkanlar da bilinmeyenler de elenir.

satırdaki rol koşul koşulun sonucu WHERE 'orta' rol = 'orta' doğru geçer 'alt' rol = 'orta' yanlış elenir NULL rol = 'orta' bilinmiyor elenir NULL rol = NULL bilinmiyor elenir NULL rol IS NULL doğru geçer
Şekil 34.2 WHERE yalnız koşulu doğru çıkan satırları geçirir. NULL ile = karşılaştırması hep "bilinmiyor" verir; NULL'u yalnız IS NULL yakalar.

Doğru sorgu:

SQL
SELECT id, oyuncu, sampiyon, rol FROM maclar WHERE rol IS NULL;
idoyuncusampiyonrol
9DenizJinxNULL
14EceLuxNULL
20MertGarenNULL

Rolü belli maçları da WHERE rol IS NOT NULL getirir. NULL'la yapılan hesaplar da NULL verir: NULL + 5 sonucu NULL'dur, çünkü bilinmeyen bir sayıya beş eklersen sonuç yine bilinmez.

Toplama fonksiyonları: COUNT, SUM, AVG, MIN, MAX

Şimdiye kadarki her sorgu satır döndürdü. Bazen satırların kendisini değil özetini istersin: "Kaç maç oynandı?" COUNT(*) satırları sayar:

SQL
SELECT COUNT(*) FROM maclar;
count
20

20 satır tek bir sayıya indi. Sütunun adı count oldu; ona da AS ile daha anlamlı bir ad verebilirsin. COUNT gibi çok satırı alıp tek bir değer üreten fonksiyonlara toplama fonksiyonu denir. Beş temel toplama fonksiyonu var:

FonksiyonNe yapar?
COUNT(*)satırları sayar
SUM(kill)değerleri toplar
AVG(kill)ortalamayı hesaplar
MIN(kill)en küçüğü bulur
MAX(kill)en büyüğü bulur
SQL
SELECT SUM(kill) AS toplam_kill, AVG(kill) AS ortalama_kill,    MIN(kill) AS en_az, MAX(kill) AS en_cokFROM maclar;
toplam_killortalama_killen_azen_cok
1286.4000000000000000114

Ortalama 6.4000000000000000 çıktı, çünkü PostgreSQL ortalamayı tam hassasiyetle verir. ROUND(değer, basamak) onu istediğin basamağa yuvarlar. Toplama fonksiyonları WHERE ile birlikte de çalışır; o zaman yalnız süzülen satırlar özetlenir. Deniz'in karnesi:

SQL
SELECT COUNT(*) AS mac, SUM(kill) AS toplam_kill,    ROUND(AVG(kill), 2) AS ortalama_kill, MAX(kill) AS en_cok_killFROM maclarWHERE oyuncu = 'Deniz';
mactoplam_killortalama_killen_cok_kill
7517.2914

MIN ve MAX yalnız sayılarla sınırlı değil; tarihte en eskiyi ve en yeniyi, metinde alfabetik ilk ve sonu bulur:

SQL
SELECT MIN(tarih) AS ilk_mac, MAX(tarih) AS son_mac FROM maclar;
ilk_macson_mac
2026-09-28 17:40:00+002026-10-10 15:45:00+00

Toplama fonksiyonları NULL'u atlar

COUNT(*) satırları sayar. COUNT(sütun) ise o sütunda NULL olmayan değerleri sayar:

SQL
SELECT COUNT(*) AS tum_maclar, COUNT(rol) AS rolu_belli FROM maclar;
tum_maclarrolu_belli
2017

Aradaki fark, rolü boş üç ARAM maçı. SUM, AVG, MIN ve MAX da NULL'ları görmezden gelir.

NULL bölümündeki <> tuzağını da şimdi sayılarla görebilirsin. Rolü orta olan 5 maç var; demek ki rolü orta olmayan 15 maç olmalı:

SQL
SELECT COUNT(*) AS orta_olmayanFROM maclarWHERE rol <> 'orta';
orta_olmayan
12

12 geldi: üç ARAM maçında NULL <> 'orta' bilinmiyor çıktı ve elendi. ARAM maçlarını da saymak istiyorsan bunu açıkça söylemelisin:

SQL
SELECT COUNT(*) AS orta_olmayanFROM maclarWHERE rol <> 'orta' OR rol IS NULL;
orta_olmayan
15

Tam sayı bölmesi

Ders 33'ün sonunda gördüğün tuzak, KDA hesaplarken kesin karşına çıkacak: JavaScript'te 7 / 2 sonucu 3.5'ti, SQL'de ise iki tam sayının bölümü yine tam sayıdır ve küsurat atılır.

Orada çözüm sayıyı 7.0 diye yazmaktı. Sütunlarda bunu yapamazsın; kill ve olum tam sayı (integer) sütunları. Onun yerine bölmeden önce 1.0 ile çarparsın:

SQL
SELECT 7 / 2 AS tam_bolme, 7 * 1.0 / 2 AS ondalikli;
tam_bolmeondalikli
33.5000000000000000

* 1.0 değeri değiştirmez ama hesabı ondalıklı sayılara taşır; sütunla da aynı çalışır: kill * 1.0 / olum. AVG bunu kendisi yapar; elle bölerken unutma.

Sık hatadivision by zero

Ne zaman çıkar? Bir sütuna bölerken o sütunda 0 olan bir satır varsa. Ece, Ashe maçında hiç ölmemiş (olum 0); her maçın KDA'sını hesaplamaya kalkınca sorgu o satırda durur. JavaScript sıfıra bölünce Infinity veriyordu (Ders 1); PostgreSQL hata verir.

SQL
SELECT sampiyon, (kill + asist) / olum AS kda FROM maclar;
Hata çıktısı
ERROR:  division by zero

Nasıl düzelir? olum yerine NULLIF(olum, 0) ile böl. NULLIF(a, b) "a, b'ye eşitse NULL ver, değilse a'nın kendisini ver" demek: olum 0 ise NULL olur, değilse olduğu gibi kalır. NULL'a bölmek hata değil, NULL verir: Ders 9'daki "Kusursuz" maç gibi, KDA tanımsız kalır.

SQL
SELECT sampiyon, olum,    ROUND((kill + asist) * 1.0 / NULLIF(olum, 0), 2) AS kdaFROM maclarWHERE oyuncu = 'Ece'ORDER BY id;
sampiyonolumkda
Jinx36.67
Thresh45.00
Kai'Sa71.29
Jinx29.50
Lux83.88
Ashe0NULL
Jinx51.80

GROUP BY: her oyuncu için ayrı hesap

Her oyuncunun kaç maç oynadığını bilmek istiyorsun. COUNT(*)'un yanına oyuncu yazınca:

SQL
SELECT oyuncu, COUNT(*) AS mac_sayisi FROM maclar;
Hata çıktısı
ERROR:  column "maclar.oyuncu" must appear in the GROUP BY clause or be used in an aggregate function

COUNT(*) 20 satırı tek satıra indiriyor. O tek satırın oyuncu hücresine kimin adı yazılacak, Deniz'in mi Ece'nin mi? PostgreSQL tahmin yürütmez, hata verir. Önce satırları oyunculara ayırman gerekiyor:

SQL
SELECT oyuncu, COUNT(*) AS mac_sayisi FROM maclar GROUP BY oyuncu;
oyuncumac_sayisi
Mert6
Deniz7
Ece7

GROUP BY oyuncu satırları oyuncu adına göre kovalara ayırdı, COUNT(*) her kovayı ayrı saydı ve her kova sonuçta tek satır oldu. Sıraya dikkat et: Mert, Deniz, Ece. GROUP BY sıralamaz; senin ekranında sıra başka olabilir. Sıra istiyorsan yine ORDER BY yazarsın:

SQL
SELECT oyuncu, COUNT(*) AS mac_sayisi, SUM(kill) AS toplam_killFROM maclarGROUP BY oyuncuORDER BY oyuncu;
oyuncumac_sayisitoplam_kill
Deniz751
Ece743
Mert634
maclar: 20 satır GROUP BY oyuncu her kova tek satır D E M D E M D E D M E D M E D E M D E M Deniz 7 satır Ece 7 satır Mert 6 satır oyuncu COUNT(*) SUM(kill) Deniz 7 51 Ece 7 43 Mert 6 34
Şekil 34.3 GROUP BY oyuncu 20 satırı üç kovaya ayırır. Toplama fonksiyonları her kovayı ayrı hesaplar ve her kova sonuçta tek satır olur.

Buradan bir kural çıkıyor: GROUP BY'lı bir sorguda SELECT'e yazdığın her sütun ya GROUP BY'da olmalı ya da bir toplama fonksiyonunun içinde. Sonuçtaki her satır bir kovadır; Deniz kovasının tek bir oyuncu adı vardır, ama tek bir şampiyonu yoktur.

Sık hatacolumn "maclar.rol" must appear in the GROUP BY clause or be used in an aggregate function

Ne zaman çıkar? Gruplarken SELECT'e, grupta tek bir değeri olmayan bir sütun yazınca. Jinx'in dört maçı var ve rolleri farklı (alt ve boş); PostgreSQL Jinx satırına hangisini yazacağını bilemez.

SQL
SELECT sampiyon, rol, COUNT(*) AS macFROM maclarGROUP BY sampiyon;
Hata çıktısı
ERROR:  column "maclar.rol" must appear in the GROUP BY clause or be used in an aggregate function

Nasıl düzelir? Mesaj iki yol söylüyor ("GROUP BY'da görünmeli ya da bir toplama fonksiyonunda kullanılmalı"): sütunu gruba ekle (GROUP BY sampiyon, rol; artık her şampiyon ve rol çifti ayrı satır olur) ya da toplama fonksiyonunun içine al (COUNT(rol) gibi). GROUP BY hiç yazmadan SELECT oyuncu, COUNT(*) yazmak da aynı hatayı verir.

Aynı kalıpla şampiyon istatistiği:

SQL
SELECT sampiyon, COUNT(*) AS mac, ROUND(AVG(kill), 1) AS ort_killFROM maclarGROUP BY sampiyonORDER BY mac DESC, sampiyon;
sampiyonmacort_kill
Jinx49.5
Ahri37.3
Garen29.5
Lee Sin25.0
Lux24.0
Annie17.0
Ashe17.0
Darius13.0
Kai'Sa15.0
Teemo12.0
Thresh11.0
Yasuo16.0

Birden çok sütuna göre de gruplayabilirsin; o zaman her farklı çift ayrı bir kova olur. Her oyuncunun zafer ve yenilgi sayısı:

SQL
SELECT oyuncu, sonuc, COUNT(*) AS macFROM maclarGROUP BY oyuncu, sonucORDER BY oyuncu, sonuc;
oyuncusonucmac
Denizyenilgi2
Denizzafer5
Eceyenilgi3
Ecezafer4
Mertyenilgi3
Mertzafer3

NULL'lar da kendi kovalarında toplanır. Rol başına maç sayısında ARAM maçları ayrı bir NULL grubu olur; ORDER BY artan sıralamada NULL'u en sona koyar:

SQL
SELECT rol, COUNT(*) AS mac FROM maclar GROUP BY rol ORDER BY rol;
rolmac
alt5
destek2
orman2
orta5
üst3
NULL3

Ders 11'de reduce ile bir diziden gruplanmış bir obje çıkarmıştın; biriktiriciyi kurup her elemanı doğru anahtara eklemek birkaç satır sürüyordu. GROUP BY aynı işi tek satırda yapıyor.

HAVING: grupları süzmek

Şampiyon tablosuna tekrar bak: Annie'nin kill ortalaması 7.0, ama tek maçtan. Tek maçlık bir ortalama şans eseri olabilir. Yalnız en az iki kez oynanan şampiyonları görmek istiyorsun. Bir koşul olduğuna göre WHERE yazarsın:

Sık hataaggregate functions are not allowed in WHERE

Ne zaman çıkar? WHERE içine COUNT, SUM, AVG gibi bir toplama fonksiyonu yazınca.

SQL
SELECT sampiyon, COUNT(*) AS macFROM maclarWHERE COUNT(*) >= 2GROUP BY sampiyon;
Hata çıktısı
ERROR:  aggregate functions are not allowed in WHERE

Nasıl düzelir? WHERE tek tek satırlara bakar; o çalışırken henüz grup yoktur, sayılacak bir şey de yoktur. Grupların üzerindeki koşulu GROUP BY'dan sonra gelen HAVING'e taşı: GROUP BY sampiyon HAVING COUNT(*) >= 2.

SQL
SELECT sampiyon, COUNT(*) AS mac, ROUND(AVG(kill), 1) AS ort_killFROM maclarGROUP BY sampiyonHAVING COUNT(*) >= 2ORDER BY ort_kill DESC, sampiyon;
sampiyonmacort_kill
Garen29.5
Jinx49.5
Ahri37.3
Lee Sin25.0
Lux24.0

HAVING grupları süzer: en az iki maçı olan beş şampiyon kaldı. Garen ile Jinx'in ortalaması eşit; sıralamanın sonundaki sampiyon onları alfabetik dizdi.

WHEREHAVING
Neye bakar?Tek tek satırlaraGruplara
Ne zaman çalışır?Gruplamadan önceGruplamadan sonra
Toplama fonksiyonuYazılamazYazılır: COUNT(*) >= 2
ÖrnekWHERE sonuc = 'zafer'HAVING SUM(kill) > 30

İkisi aynı sorguda birlikte de kullanılır: önce WHERE satırları süzer, kalanlar gruplanır, sonra HAVING grupları süzer. Bir sonraki bölümdeki sorgu tam olarak bunu yapıyor.

Sorgu hangi sırayla çalışır?

Şimdi hepsini bir araya getir. ARAM dışındaki maçlarda, en az iki kez oynanan şampiyonlardan kill ortalaması en yüksek iki tanesi:

SQL
SELECT sampiyon, COUNT(*) AS mac, ROUND(AVG(kill), 1) AS ort_killFROM maclarWHERE rol IS NOT NULLGROUP BY sampiyonHAVING COUNT(*) >= 2ORDER BY ort_kill DESCLIMIT 2;
sampiyonmacort_kill
Jinx38.0
Ahri37.3

Sorguyu yukarıdan aşağı yazıyorsun, ama PostgreSQL onu bu sırayla çalıştırmaz. SELECT en üstte yazılır, oysa beşinci sırada çalışır:

Yazdığın sıra Çalıştığı sıra 5 SELECT sampiyon, COUNT(*), … 1 FROM maclar 2 WHERE rol IS NOT NULL 3 GROUP BY sampiyon 4 HAVING COUNT(*) >= 2 6 ORDER BY ort_kill DESC 7 LIMIT 2 1 FROM 20 satır 2 WHERE 17 satır 3 GROUP BY 12 grup 4 HAVING 3 grup 5 SELECT 3 satır, 3 sütun 6 ORDER BY sıralandı 7 LIMIT 2 satır
Şekil 34.4 Solda sorgunun yazıldığı sıra, yanında her satırın çalıştığı adım. Sağda aynı adımlar çalıştıkları sırayla; her adımın yanında o adımdan sonra elde kalan.

Bu sırayı bilince dersteki hataların çoğu kendiliğinden anlam kazanır:

  • WHERE (2) gruplamadan (3) önce çalışır; bu yüzden içine COUNT(*) yazılamaz. Grup koşulu HAVING'e (4) gider.
  • SELECT (5), WHERE'den sonra çalışır. AS ile verdiğin takma adlar WHERE çalışırken henüz yoktur. PostgreSQL'de HAVING de takma adı tanımaz; orada da hesabın kendisini (COUNT(*)) yazarsın.
  • ORDER BY (6), SELECT'ten sonra çalışır; takma adı (ort_kill) orada rahatça kullanabilirsin.
  • LIMIT (7) en sonda çalışır. "İlk 2" her zaman sıralanmış sonucun ilk ikisidir.
Sık hatacolumn "katki" does not exist

Ne zaman çıkar? SELECT'te verdiğin takma adı WHERE'de (ya da HAVING'de) kullanınca. WHERE çalışırken katki adı henüz verilmemiştir.

SQL
SELECT oyuncu, sampiyon, kill + asist AS katkiFROM maclarWHERE katki >= 20;
Hata çıktısı
ERROR:  column "katki" does not exist

Nasıl düzelir? WHERE'de hesabın kendisini yaz; takma adı ORDER BY'da kullanmaya devam edebilirsin:

SQL
SELECT oyuncu, sampiyon, kill + asist AS katkiFROM maclarWHERE kill + asist >= 20ORDER BY katki DESC, sampiyon;
oyuncusampiyonkatki
DenizJinx36
EceLux31
MertGaren28
EceJinx20
MertLee Sin20
EceThresh20

Sorgularını bir dosyada sakla

Sorgular Neon'un History listesinde de durur, ama asıl yerleri bir dosya: Git'e kaydedilir, istediğin an yeniden çalıştırırsın. Ders 33'teki C:\kod\veritabani klasörünü VS Code'da aç ve iki dosya ekle: dersin başındaki betiği maclar.sql adıyla kaydet, işine yarayan sorguları ders34.sql'e yaz. VS Code .sql uzantısını tanır ve anahtar sözcükleri renklendirir.

ders34.sql — veritabani — Visual Studio Code
ders34.sql
-- Ders 34: maç geçmişini sorgulamak-- Sorguları Neon'da tek tek çalıştır. -- Deniz'in kazandığı maçlarSELECT sampiyon, kill, olum, asistFROM maclarWHERE oyuncu = 'Deniz'    AND sonuc = 'zafer'ORDER BY kill DESC; -- Oyuncu başına maç sayısı ve killSELECT oyuncu, COUNT(*) AS mac_sayisi,    SUM(kill) AS toplam_killFROM maclarGROUP BY oyuncuORDER BY oyuncu; -- En az 2 kez oynanan şampiyonlarSELECT sampiyon, COUNT(*) AS mac,    ROUND(AVG(kill), 1) AS ort_killFROM maclarGROUP BY sampiyonHAVING COUNT(*) >= 2ORDER BY ort_kill DESC, sampiyon;
⎇ masterLn 1, Col 1Spaces: 4UTF-8SQL

-- ile başlayan yorumlar her sorgunun ne yaptığını anlatıyor ve her sorgu ; ile bitiyor. Sorgular dersteki gibi cümleciklerine bölünmüş; uzun bir SELECT listesini de virgülden sonra alt satıra geçip 4 boşluk içeri alarak sürdürebilirsin. Birini çalıştırmak için seçip SQL Editor'e kopyala.

Sık hatalar

Dersteki hataların çoğunu ilgili bölümlerde gördün. Birkaçı daha; hepsi hata rehberinde de duruyor.

Sık hatasyntax error at or near "WHERE"

Ne zaman çıkar? Cümlecikleri yanlış sırayla yazınca. Aşağıda WHERE, ORDER BY'dan sonra yazılmış.

SQL
SELECT * FROM maclar ORDER BY kill DESC WHERE oyuncu = 'Ece';
Hata çıktısı
ERROR:  syntax error at or near "WHERE"

Nasıl düzelir? Yazım sırası sabit: SELECT, FROM, WHERE, GROUP BY, HAVING, ORDER BY, LIMIT, OFFSET. Kullanmadığın cümleciği atlarsın, ama sıralarını değiştiremezsin. (Tek istisna sondaki ikili: PostgreSQL OFFSET'i LIMIT'ten önce yazmana da izin verir.)

Sık hatacolumn "sampion" does not exist

Ne zaman çıkar? Sütun adını yanlış yazınca.

SQL
SELECT oyuncu, sampion FROM maclar;
Hata çıktısı
ERROR:  column "sampion" does not existHINT:  Perhaps you meant to reference the column "maclar.sampiyon".

Nasıl düzelir? HINT (ipucu) satırını oku: PostgreSQL adı benzeyen sütunu öneriyor, "maclar.sampiyon sütununu mu kastettin?" diyor. Sütun adlarından emin değilsen SELECT * FROM maclar LIMIT 1; ile tabloya bir göz at; bu, *'ın yerinde kullanımlarından biri.

Sık hatainvalid input syntax for type boolean: "Lux"

Ne zaman çıkar? "Ahri ya da Lux" cümlesini sözcük sözcük çevirince.

SQL
SELECT oyuncu, sampiyonFROM maclarWHERE sampiyon = 'Ahri' OR 'Lux';
Hata çıktısı
ERROR:  invalid input syntax for type boolean: "Lux"

Nasıl düzelir? OR'un iki yanı da ayrı birer koşul olmalı. Sağdaki 'Lux' tek başına bir koşul değil; PostgreSQL onu doğru/yanlış değerine (boolean) çevirmeye çalışıyor ve başaramıyor. Doğrusu sampiyon = 'Ahri' OR sampiyon = 'Lux' ya da kısaca sampiyon IN ('Ahri', 'Lux').

Görevler

Görevleri SQL Editor'de yap; çözümlerini ders34.sql'e de ekleyebilirsin. Her görevde beklenen sonuç var: seninki satır satır aynı olmalı. Önce kendin dene, takılırsan ipuçlarını sırayla aç, çözüme en son bak.

Görev 1

Uzun maçlar

35 dakika ya da daha uzun süren maçları listele. Sütunlar: oyuncu, sampiyon, sure_dk. En uzun maç en üstte olsun; süresi eşit maçlar oyuncu adına göre A'dan Z'ye dizilsin. Beklenen sonuç:

oyuncusampiyonsure_dk
EceJinx40
EceThresh36
DenizLux35
MertTeemo35
İpucu 1

Hangi karşılaştırma işareti 35'in kendisini de içine alır: > mı, >= mi?

İpucu 2

ORDER BY'a iki sütun yaz: önce süre (büyükten küçüğe), sonra oyuncu.

Çözümü göster
SQL
SELECT oyuncu, sampiyon, sure_dkFROM maclarWHERE sure_dk >= 35ORDER BY sure_dk DESC, oyuncu;
oyuncusampiyonsure_dk
EceJinx40
EceThresh36
DenizLux35
MertTeemo35

Sınır denemesi: >= yerine > yazarsan 35 dakikalık iki maç kaybolur ve 2 satır kalır; "35 ve üstü" ile "35'ten uzun" aynı şey değil. İkinci sıralama sütununu silersen Deniz ile Mert'in 35 dakikalık maçları sende ters sırada gelebilir.

Görev 2

Destek ve orman

Koçun, destek ya da orman oynanan maçlardan asist sayısı 10 ile 15 arasında olanları istiyor; 10 ve 15 de dahil. Sütunlar: oyuncu, sampiyon, rol, asist; en çok asist üstte. Beklenen sonuç:

oyuncusampiyonrolasist
DenizLuxdestek15
MertLee Sinorman14
MertLee Sinorman10
İpucu 1

Rol için "şu ikisinden biri" diyen kısa yolu, asist için iki ucu dahil olan aralığı kullan.

İpucu 2

rol IN ('destek', 'orman') ve asist BETWEEN 10 AND 15; ikisini AND ile bağla.

İpucu 3

Sonuçta Ece'nin Thresh maçı (19 asist) da varsa rol = 'destek' OR rol = 'orman' AND ... yazmışsın: AND önce bağlandı. IN kullan ya da OR'lu kısmı paranteze al.

Çözümü göster
SQL
SELECT oyuncu, sampiyon, rol, asistFROM maclarWHERE rol IN ('destek', 'orman') AND asist BETWEEN 10 AND 15ORDER BY asist DESC;
oyuncusampiyonrolasist
DenizLuxdestek15
MertLee Sinorman14
MertLee Sinorman10

Sınır denemesi: listedeki iki maç tam sınırda (15 ve 10). BETWEEN iki ucu da kapsadığı için geldiler; asist > 10 AND asist < 15 yazsaydın yalnız 14 asistli maç kalırdı.

Görev 3

Şampiyon arama

Bir arama kutusu yapıyorsun ve kullanıcı kutuya JI yazdı. Adında bu harfler geçen şampiyonların maçlarını büyük-küçük harf fark etmeden bul, ama ARAM maçlarını (rolü boş olanlar) alma. Sütunlar: id, oyuncu, sampiyon, rol; en yeni maç üstte. Beklenen sonuç 3 satır; ilki 19 · Ece · Jinx · alt, sonuncusu 2 · Ece · Jinx · alt.

İpucu 1

Büyük-küçük harfe bakmayan arama ILIKE. Harflerin adın herhangi bir yerinde olabileceğini iki yanlarına koyduğun % ile söyle.

İpucu 2

ARAM maçlarında rol NULL. NULL'u = ya da <> ile değil, IS ile denetle.

İpucu 3

"En yeni üstte" için tarih'e göre büyükten küçüğe sırala.

Çözümü göster
SQL
SELECT id, oyuncu, sampiyon, rolFROM maclarWHERE sampiyon ILIKE '%ji%' AND rol IS NOT NULLORDER BY tarih DESC;
idoyuncusampiyonrol
19EceJinxalt
11EceJinxalt
2EceJinxalt

Sınır denemeleri: rol IS NOT NULL'u unutursan Deniz'in ARAM'daki Jinx maçı (id 9) da gelir ve 4 satır olur. ILIKE yerine LIKE '%JI%' boş tablo döndürür. Kutu boşken kalıp '%%' olur ve ARAM dışındaki 17 maçın hepsi gelir; çoğu uygulamada istenen budur, ama bilerek seç.

Görev 4

Sayfalı maç geçmişi

Maç geçmişi ekranın her sayfada 6 maç gösteriyor, en yeni maç en üstte. 2. sayfayı getiren sorguyu yaz. Sütunlar: id, oyuncu, sampiyon, sonuc. Beklenen sonuç 6 satır: ilki 14 · Ece · Lux · yenilgi, sonuncusu 9 · Deniz · Jinx · zafer.

İpucu 1

İlk sayfa en yeni 6 maç. İkinci sayfa için o 6 maçı atlaman gerekiyor.

İpucu 2

ORDER BY tarih DESC, ardından LIMIT 6 OFFSET ve atlanacak satır sayısı.

Çözümü göster
SQL
SELECT id, oyuncu, sampiyon, sonucFROM maclarORDER BY tarih DESCLIMIT 6 OFFSET 6;
idoyuncusampiyonsonuc
14EceLuxyenilgi
13MertLee Sinzafer
12DenizYasuoyenilgi
11EceJinxzafer
10MertDariusyenilgi
9DenizJinxzafer

Sınır denemesi: 20 maç 6'şarlık sayfalara bölününce son sayfa eksik kalır. 4. sayfa yalnız 2 satır getirir:

SQL
SELECT id, oyuncu, sampiyon, sonucFROM maclarORDER BY tarih DESCLIMIT 6 OFFSET 18;
idoyuncusampiyonsonuc
2EceJinxzafer
1DenizAhrizafer

Beşinci sayfa (OFFSET 24) boş bir tablo döndürür; hata değil. 6'dan az satır geldiyse son sayfadasın; uygulaman "daha fazla göster" düğmesini buna bakarak gizleyebilir. Burada her maçın tarih'i farklı, yani sıra benzersiz; aynı anda başlayan maçlar olabilseydi ORDER BY tarih DESC, id DESC yazardın.

Görev 5

En çok kazanılan şampiyonlar

Zafer sayısına göre ilk 3 şampiyonu bul. Her biri için zafer sayısını, bu zaferlerin ortalama süresini (bir basamağa yuvarlanmış) ve en kısa zaferin süresini göster. Sütunlar: sampiyon, zafer_sayisi, ort_sure, en_kisa; zafer sayısı eşit olanlar adlarına göre A'dan Z'ye dizilsin. Beklenen sonuç:

sampiyonzafer_sayisiort_sureen_kisa
Jinx327.019
Ahri230.029
Garen227.521
İpucu 1

Önce yalnız zaferleri süz, sonra kalan satırları şampiyona göre grupla. Her kova için üç toplama fonksiyonu gerekiyor: sayan, ortalama alan ve en küçüğü bulan.

İpucu 2

COUNT(*) AS zafer_sayisi, ROUND(AVG(sure_dk), 1) AS ort_sure, MIN(sure_dk) AS en_kisa. Sıralamada zafer_sayisi adını kullanabilirsin, çünkü ORDER BY, SELECT'ten sonra çalışır.

İpucu 3

ORDER BY zafer_sayisi DESC, sampiyon ve en sonda LIMIT 3.

Çözümü göster
SQL
SELECT sampiyon, COUNT(*) AS zafer_sayisi,    ROUND(AVG(sure_dk), 1) AS ort_sure, MIN(sure_dk) AS en_kisaFROM maclarWHERE sonuc = 'zafer'GROUP BY sampiyonORDER BY zafer_sayisi DESC, sampiyonLIMIT 3;
sampiyonzafer_sayisiort_sureen_kisa
Jinx327.019
Ahri230.029
Garen227.521

Sınır denemeleri: WHERE satırını unutursan yine Jinx, Ahri ve Garen gelir, ama sayılar 4, 3 ve 2 olur: onlar zafer değil, maç sayısı. Süreler de bütün maçlardan hesaplanır; Ahri'nin en kısa maçı 27 dakikalık yenilgisi olur. Adlara bakıp "doğru" deme, sayıları da denetle. Sıralamanın sonundaki sampiyon'u silersen Garen, Ahri'nin önüne geçebilir; LIMIT 2 yazarsan da ikisinden biri, eşit oldukları hâlde düşer. Eşitlik sınıra denk gelirse kimin kalacağını senin yazdığın sıralama belirler.

Görev 6

Oyuncu karnesi: KDA

En az 7 maç oynamış oyuncuların maç sayısını ve genel KDA'sını hesapla. Genel KDA: bütün maçlardaki kill ve asist toplamının ölüm toplamına bölümü, iki basamağa yuvarlanmış. En yüksek KDA üstte. Sütunlar: oyuncu, mac, kda. Beklenen sonuç:

oyuncumackda
Ece74.38
Deniz73.90
İpucu 1

Oyuncu başına hesap: GROUP BY oyuncu. "En az 7 maç" bir grup koşulu; hangi cümleciğe yazılır?

İpucu 2

(SUM(kill) + SUM(asist)) / SUM(olum) tam sayı bölmesi yapar ve 4 ile 3 verir. Bölmeden önce * 1.0 ile ondalığa geç, sonucu ROUND(..., 2) ile yuvarla.

İpucu 3

HAVING COUNT(*) >= 7, sonra ORDER BY kda DESC.

Çözümü göster
SQL
SELECT oyuncu, COUNT(*) AS mac,    ROUND((SUM(kill) + SUM(asist)) * 1.0 / SUM(olum), 2) AS kdaFROM maclarGROUP BY oyuncuHAVING COUNT(*) >= 7ORDER BY kda DESC;
oyuncumackda
Ece74.38
Deniz73.90

Sınır denemeleri: HAVING COUNT(*) > 7 boş tablo döndürür, çünkü kimse 7'den fazla oynamamış; sınır tam 7'de. Mert 6 maçla listede yok; HAVING satırını silersen o da 2.37 ile gelir. Her maçın KDA'sını ayrı hesaplamaya kalkarsan Ece'nin ölümsüz Ashe maçında division by zero hatasına çarparsın; toplamlarla hesaplamak bunu da önler, çünkü kimsenin ölüm toplamı 0 değil.

Kendini sına

Soru 1

Bu sorgu kaç satır döndürür? Neden?

SQL
SELECT * FROM maclar WHERE rol = NULL;
Cevabı göster

Sıfır. NULL "bilinmiyor" demek; bir değerin bilinmeyen bir şeye eşit olup olmadığı da bilinmez, karşılaştırmanın sonucu NULL olur. WHERE yalnız sonucu doğru (true) çıkan satırları geçirir. Doğrusu WHERE rol IS NULL; 3 satır gelir.

Soru 2

sampiyon LIKE '_a%' bu tablodaki hangi şampiyonları bulur?

Cevabı göster

İkinci harfi a olanları: Garen, Darius, Kai'Sa ve Yasuo. _ tam bir karakterin yerini tutar (ilk harf ne olursa olsun), ardından a gelmeli, % da geri kalanı karşılar.

Soru 3

Bu sorgu neden hata verir? Her sonuç için ayrı sayı istiyorsan nasıl düzeltirsin?

SQL
SELECT sonuc, COUNT(*) FROM maclar;
Cevabı göster

COUNT(*) bütün tabloyu tek satıra indirir, sonuc ise 20 satırdan gelir; tek satıra hangi sonucun yazılacağı belli değildir. PostgreSQL column "maclar.sonuc" must appear in the GROUP BY clause or be used in an aggregate function der. Sonuna GROUP BY sonuc eklersen zafer ve yenilgi için birer satır gelir.

Soru 4

ORDER BY tarih DESC LIMIT 5 OFFSET 10 hangi maçları getirir?

Cevabı göster

En yeniden eskiye sıralı listede 11. ile 15. maçları: ilk 10 satırı atlar, sonraki 5'ini alır. 5'erli sayfalarda bu 3. sayfadır.

Soru 5

WHERE mi, HAVING mi? (a) Yalnız Ece'nin maçlarını say. (b) En az 4 zaferi olan oyuncuları göster.

Cevabı göster

(a) WHERE oyuncu = 'Ece': tek tek satırlara bakan bir koşul, gruplamadan önce çalışır. (b) Zaferleri önce WHERE sonuc = 'zafer' ile süzer, GROUP BY oyuncu ile gruplarsın; "en az 4" ise grubun sayısına bakan bir koşul, yani HAVING:

SQL
SELECT oyuncu, COUNT(*) AS zaferFROM maclarWHERE sonuc = 'zafer'GROUP BY oyuncuHAVING COUNT(*) >= 4ORDER BY oyuncu;
oyuncuzafer
Deniz5
Ece4

Soru 6

Bu sorgu çalışır mı? Çalışmazsa hangi satır yüzünden?

SQL
SELECT sampiyon, kill + asist AS katkiFROM maclarWHERE katki > 25ORDER BY katki DESC;
Cevabı göster

Çalışmaz: column "katki" does not exist. Suçlu 3. satır. WHERE, SELECT'ten önce çalışır; takma ad o anda henüz verilmemiştir. 4. satırdaki ORDER BY ise sorunsuz, çünkü SELECT'ten sonra çalışır. Düzeltme: WHERE kill + asist > 25.

Soru 7

Bu sorgu maclar tablosunda hangi üç sayıyı döndürür?

SQL
SELECT COUNT(*) AS a, COUNT(rol) AS b, SUM(kill) / COUNT(*) AS cFROM maclar;
Cevabı göster

a 20: COUNT(*) bütün satırları sayar. b 17: COUNT(rol) yalnız rolü NULL olmayanları sayar, üç ARAM maçı dışarıda kalır. c 6: toplam kill 128, maç sayısı 20, ama ikisi de tam sayı olduğu için bölümün küsuratı atılır ve 6.4 yerine 6 çıkar. Doğru ortalama için AVG(kill) ya da SUM(kill) * 1.0 / COUNT(*) yazarsın.

Kayıt noktası

C:\kod\veritabani klasörüne maclar.sql ve ders34.sql eklendi. Git'i bu klasörde Ders 33'te başlattın; şimdi yalnız yeni bir kayıt alacaksın. VS Code terminalinde, bu klasördeyken:

Bir sonraki derste veriyi okumakla kalmayıp değiştireceksin: INSERT, UPDATE ve DELETE ile maç ekleyip düzelteceksin, kısıtlarla tabloyu bozuk veriye karşı koruyacaksın ve WHERE'siz bir DELETE'in neden felaket olduğunu güvenli bir ortamda göreceksin.

İşaret yalnızca bu tarayıcıda saklanır, kimseye gönderilmez.