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

Blok 5 · Ders 35

Veriyi değiştirmek ve tablo tasarımı

INSERT, UPDATE, DELETE, kısıtlar (NOT NULL, UNIQUE, CHECK, DEFAULT), tablo değiştirmek ve WHERE'siz silmenin felaketi.

Bu dersin sonunda

  • INSERT ile tek ve çok satır ekleyip RETURNING ile veritabanının verdiği id'yi aynı komutta geri alabilirsin
  • UPDATE … SET … WHERE ile yalnız istediğin satırları değiştirebilir, DELETE'ten önce aynı WHERE'i SELECT ile deneyebilirsin
  • NOT NULL, UNIQUE, CHECK ve DEFAULT ile bozuk verinin tabloya girmesini engelleyebilir, ihlal mesajından hangi kuralın çiğnendiğini bulabilirsin
  • ALTER TABLE ile dolu bir tabloya sütun ve kural ekleyebilir, sütunun adını değiştirebilir ya da onu silebilirsin
  • BEGIN, COMMIT ve ROLLBACK ile birbirine bağlı değişiklikleri ya hep birlikte ya hiç yapabilirsin
  • Riskli bir işten önce Neon'da kayıt noktası alabilir, pg_dump ile veritabanını bir dosyaya yedekleyebilirsin
Bu sayfada
  1. Hazırlık: iki tablo
  2. INSERT: satır eklemek
  3. UPDATE: var olan satırı değiştirmek
  4. DELETE ve WHERE'siz felaket
  5. Kısıtlar: bozuk veriyi kapıda durdurmak
  6. ALTER TABLE: dolu tabloyu yerinde değiştirmek
  7. Transaction: ya hep ya hiç
  8. Yedek almak
  9. Sık hatalar
  10. Görevler
  11. Kendini sına
  12. Kayıt noktası

Ders 34'te veriyi yalnız okudun. SELECT tabloya dokunmaz: ne kadar yanlış yazarsan yaz, en kötü ihtimalle yanlış bir liste görürsün. Bu derste kalemi eline alıyorsun: satır eklemek (INSERT), değiştirmek (UPDATE) ve silmek (DELETE). Maç geçmişine bugünkü maçı ekleyecek, yanlış girilmiş bir skoru düzeltecek, yarım kalmış bir kaydı sileceksin. Ders 28'de API'ne ekle, güncelle ve sil uç noktaları yazmıştın; veritabanındaki karşılıkları bu üç komut.

Yazma gücünün bir bedeli var: tek bir unutulmuş sözcük bütün tabloyu silebilir ve Postgres "emin misin?" diye sormaz. Bu yüzden dersin ikinci yarısı güvenlik ağları üzerine: bozuk veriyi kapıda durduran kurallar (kısıtlar), yarım kalan işi geri saran transaction ve her şey ters giderse döneceğin yedek. Arada dolu bir tablonun yapısını ALTER TABLE ile yerinde değiştirmeyi de öğreneceksin.

Hazırlık: iki tablo

Bu derste iki tabloyla çalışacaksın. Birincisi Ders 34'teki maclar. Neon'da duruyorsa ona dokunma. Silindiyse ya da değiştirdiysen C:\kod\veritabani\maclar.sql dosyasını SQL Editor'e yapıştırıp çalıştır; tablo 20 maçla baştan kurulur. Dosya elinde değilse betik aşağıda:

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');

İkincisi yeni: oyuncu hesapları. Her satırda kullanıcı adı, altın ve hesap seviyesi var. Bu tabloyu bilerek kuralsız kuruyorsun: Ders 33'teki gibi her satırın otomatik artan bir id'si var, öteki sütunlara ne yazarsan girer. Bu serbestliğin bedelini dersin ortasında göreceksin.

SQL
CREATE TABLE hesaplar (    id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    ad text,    altin integer,    seviye integer);

Daha önce aynı adla bir tablo açtıysan Ders 33'ten tanıdığın relation "hesaplar" already exists hatasını alırsın. Çoğu zaman sebebi, bu dersi daha önce yarıda bırakmış olmandır. Dersteki numaraları birebir görmek istiyorsan DROP TABLE hesaplar; ile eskisini silip tabloyu yeniden kur; aynısı birazdan kuracağın dukkan için de geçerli. Düzenleyicide eski bir CREATE TABLE kaldıysa New Query ile temizle.

INSERT: satır eklemek

Ders 33'te INSERT ile ilk satırlarını eklemiştin. Kalıp üç parçalı: hangi tabloya (INSERT INTO maclar), hangi sütunlara ((oyuncu, sampiyon, …)), hangi değerler (VALUES (...)). Değerler sütun listesindeki sırayla eşleşir: birinci değer birinci sütuna, ikincisi ikinciye.

Veritabanı ne verdi? RETURNING

Deniz bugün bir maç daha oynadı: Lux, orta koridor, zafer, 5/2/13. Maçı maclar tablosuna eklediğinde ona hangi id'nin verileceğini bilmiyorsun; o numarayı sen yazmıyorsun, Postgres seçiyor. Ama çoğu zaman o numaraya hemen ihtiyacın olur.

Ders 28'deki API'n yeni şampiyonun id'sini kendisi sayıyordu (sonId = sonId + 1) ve 201 yanıtıyla kaydın tamamını geri gönderiyordu; istemci yeni numarayı o yanıttan öğreniyordu. Veritabanında numarayı Postgres verir, sen de onu RETURNING ile öğrenirsin:

SQL
INSERT INTO maclar (oyuncu, sampiyon, rol, sonuc, kill, olum, asist, sure_dk, tarih)VALUES ('Deniz', 'Lux', 'orta', 'zafer', 5, 2, 13, 30, '2026-10-11 21:10+03')RETURNING id, oyuncu, sampiyon;
idoyuncusampiyon
21DenizLux

Tabloda 20 maç vardı; yeni maç 21 numarayı aldı. RETURNING komutun sonuna eklenir ve "yazdığın satırın şu sütunlarını bana geri göster" der. Sonuç bir SELECT sonucuna benziyor ama hiçbir şey sorgulanmadı: satır yazıldı ve aynı komutta geri okundu. İkinci bir sorguya gerek kalmadı.

Tek komutla çok satır

Şimdi boş hesaplar tablosunu doldur. Her satır için ayrı INSERT yazmak zorunda değilsin: VALUES'tan sonra satırları virgülle ayırarak sıralarsın, noktalı virgül yalnız en sonda:

SQL
INSERT INTO hesaplar (ad, altin, seviye)VALUES    ('Ahri', 300, 1),    ('Lux', 450, 3),    ('Garen', 120, 2),    ('Jinx', 800, 5),    ('Teemo', 50, 1)RETURNING id, ad;
idad
1Ahri
2Lux
3Garen
4Jinx
5Teemo

RETURNING bu kez beş satır döndürdü: her yeni hesabın numarası tek komutta elinde. RETURNING * yazarsan bütün sütunlar gelir. Denerken işe yarar; kodunda ise Ders 34'te SELECT * için gördüğün sebeplerle istediğin sütunları tek tek yaz.

Yazmadığın sütun NULL olur

SQL
INSERT INTO hesaplar (ad)VALUES ('Yasuo')RETURNING id, ad, altin, seviye;
idadaltinseviye
6YasuoNULLNULL

Sütun listesine yalnız ad yazdın; Postgres öteki sütunlara NULL koydu. Yasuo'nun altını sıfır değil, bilinmiyor. Kuralsız tablo buna itiraz etmez. Dersin ortasında DEFAULT ve NOT NULL ile bunu kapatacaksın.

Sütun sayısıyla değer sayısı tutmazsa Postgres satırı hiç yazmaz:

SQL
INSERT INTO hesaplar (ad, altin, seviye)VALUES ('Annie', 200);
Hata çıktısı
ERROR:  INSERT has more target columns than expressions

target columns hedef sütunlar, expressions verdiğin değerler: "üç sütun söyledin ama iki değer verdin". Ders 33'te tersini görmüştün: INSERT has more expressions than target columns.

Bir satır bozuksa hiçbiri girmez

Çok satırlı bir INSERT'te satırlardan biri bozuk olursa ne olur? Zed'in altınını yazıyla yaz:

SQL
INSERT INTO hesaplar (ad, altin, seviye)VALUES    ('Annie', 200, 1),    ('Zed', 'yüz', 2);
Hata çıktısı
ERROR:  invalid input syntax for type integer: "yüz"

Ders 33'ten tanıdığın mesaj: 'yüz' sayıya çevrilemiyor. Asıl ilginç olan Annie: onun satırında sorun yoktu, ama o da eklenmedi.

SQL
SELECT COUNT(*) AS hesap_sayisi FROM hesaplar;
hesap_sayisi
6

Tek bir komut ya bütünüyle çalışır ya hiç: çok satırlı bir INSERT'te tek satır bile bozuksa hiçbiri yazılmaz. Bu "ya hep ya hiç" fikrine dersin sonunda, transaction'da döneceksin.

Sık hatasyntax error at or near "("

Ne zaman çıkar? Çok satırlı INSERT'te iki satırın arasına virgül koymayı unutunca.

SQL
INSERT INTO hesaplar (ad, altin, seviye)VALUES    ('Vex', 100, 1)    ('Zed', 200, 2);
Hata çıktısı
ERROR:  syntax error at or near "("

syntax error at or near "…" Postgres'in komutu okurken nerede takıldığını söyler: burada ikinci satırın açılan parantezi. Sorun çoğu zaman gösterilen yerin hemen önündedir: bir önceki satırın sonunda virgül eksik.

Nasıl düzelir? Her satırdan sonra virgül koy, yalnız son satırdan sonra ; gelsin.

UPDATE: var olan satırı değiştirmek

Ahri bir etkinlikten ödül aldı ve altını 650 oldu; Garen seviye atlayıp 100 altın ödül kazandı. Satırı silip yeniden eklemek olmaz: id'si değişir ve o numaraya bağlı her şey kopar. Satırı yerinde değiştirirsin. Ders 1'deki envanter yuvasını hatırla: yuva aynı kalır, içindeki değişir. UPDATE'te de satır aynı kalır, hücredeki değer değişir.

SQL
UPDATE hesaplarSET altin = 650WHERE ad = 'Ahri'RETURNING id, ad, altin;
idadaltin
1Ahri650
ParçaAnlamı
UPDATE hesaplarhangi tablo
SET altin = 650hangi sütuna hangi yeni değer
WHERE ad = 'Ahri'hangi satırlar; Ders 34'teki WHERE'in aynısı
RETURNING id, ad, altindeğişen satırları göster (isteğe bağlı)

Eski değerden hesaplamak

Yeni değeri çoğu zaman eskisinden hesaplarsın. Ders 1'deki hp = hp - 10 satırını hatırla: sağ taraf önce hesaplanır, sonuç soldaki kutuya konur. SET de aynısını yapar. Birden çok sütunu virgülle ayırıp aynı komutta değiştirebilirsin:

SQL
UPDATE hesaplarSET altin = altin + 100, seviye = seviye + 1WHERE ad = 'Garen'RETURNING ad, altin, seviye;
adaltinseviye
Garen2203

Birden çok satır

WHERE kaç satır seçerse UPDATE o kadar satırı değiştirir. Seviyesi 1 olan herkese 50 altın hoş geldin hediyesi:

SQL
UPDATE hesaplarSET altin = altin + 50WHERE seviye = 1RETURNING ad, altin;
adaltin
Teemo100
Ahri700

İki şey dikkatini çeksin. Birincisi, Yasuo listede yok: seviyesi NULL, NULL = 1 "bilinmiyor" çıkar ve WHERE yalnız doğru çıkan satırları seçer (ısınmadaki ilk soru). İkincisi, satırlar id sırasıyla gelmedi. RETURNING'in sırası garanti değildir; sıralı bir liste istiyorsan ardından ORDER BY'lı bir SELECT çalıştır.

Hiçbir satır tutmazsa

SQL
UPDATE hesaplarSET altin = 0WHERE ad = 'ahri'RETURNING ad, altin;
adaltin

Sonuç boş: hiçbir satır değişmedi. Hata da yok. Metin karşılaştırması büyük-küçük harfe duyarlıdır, 'ahri' ile 'Ahri' farklı metinlerdir. UPDATE ve DELETE hiçbir satır bulamayınca şikâyet etmez; bu yüzden RETURNING ile neyin değiştiğine bakmak iyi bir alışkanlık.

Yanlış bir değer sessizce boş sonuç verir; yanlış bir sütun adı ise hata verir. SET'teki bir yazım hatası Ders 33'ten tanıdığın mesajı getirir:

SQL
UPDATE hesaplarSET altn = 500WHERE ad = 'Lux';
Hata çıktısı
ERROR:  column "altn" of relation "hesaplar" does not exist

Yanlış ad WHERE'de olursa mesaj column "adi" does not exist olur ve Postgres altına bir tahmin ekler: HINT: Perhaps you meant to reference the column "hesaplar.ad". ("Şu sütunu mu demek istedin?"). İki durumda da hiçbir satır değişmez.

Tek bir satırı kesin hedeflemek

Ece, maclar'daki 16 numaralı Ashe maçında aslında bir kez öldüğünü fark etti: skor 7/0/12 değil, 7/1/12. Ders 33'te iki Garen satırı tıpatıp aynıyken "şu satır" diyemiyordun; birincil anahtar tam bu an için vardı:

SQL
UPDATE maclarSET olum = 1WHERE id = 16RETURNING id, oyuncu, sampiyon, kill, olum, asist;
idoyuncusampiyonkillolumasist
16EceAshe7112

WHERE oyuncu = 'Ece' AND sampiyon = 'Ashe' de bugün aynı satırı bulurdu. Ama Ece yarın bir Ashe maçı daha oynarsa o WHERE iki satırı birden değiştirir. Tek bir satırı düzeltirken WHERE'de birincil anahtarı kullan; iki satırın id'si hiçbir zaman aynı olamaz.

DELETE ve WHERE'siz felaket

Satır silmenin kalıbı üç komutun en kısası:

SQL
DELETE FROM tablo WHERE kosul;

Kısa olması tehlikeli olmasının da sebebi: WHERE kısmını unutursan da komut geçerlidir ve tablodaki her satıra uygulanır.

Önce SELECT ile dene

Silmeden önce silmek istediğin satırları aynı WHERE ile seç ve gözünle gör. Doğru satırlar geldiyse yalnız baştaki SELECT … kısmını DELETE ile değiştir; WHERE'e dokunma.

Yasuo'nun hesabı yarım kalmış; altını ve seviyesi bilinmiyor. Onu silmek istiyorsun:

SQL
SELECT id, ad, altin, seviyeFROM hesaplarWHERE altin IS NULL;
idadaltinseviye
6YasuoNULLNULL

Tek satır, beklediğin satır. Şimdi baştaki SELECT satırını DELETE yap:

SQL
DELETE FROM hesaplarWHERE altin IS NULLRETURNING id, ad;
idad
6Yasuo
  1. Değiştirmek ya da silmek istediğin satırları SELECT … WHERE … ile seç.
  2. Gelen satırlara ve sayısına bak. Fazla ya da eksikse WHERE'i düzelt, yeniden seç.
  3. SELECT … kısmını DELETE (ya da UPDATE … SET …) ile değiştir; WHERE'i aynen bırak.
  4. RETURNING ile gerçekten neyin silindiğine ya da değiştiğine bak.

WHERE'siz DELETE

Şimdi bu dersin en tehlikeli satırını bir güvenlik ağının içinde çalıştıracaksın. BEGIN ve ROLLBACK'i dersin sonunda ayrıntılı göreceksin; şimdilik BEGIN'i "deneme başlıyor", ROLLBACK'i "denemeyi bütünüyle geri al" diye oku. Denemeyi maclar tablosunda yap: bir şey ters giderse maclar.sql'i yeniden çalıştırıp 20 maçı geri getirirsin, yani bir güvence daha var. Dört satırın hepsini birlikte düzenleyiciye yaz ve tek seferde çalıştır:

SQL
BEGIN;DELETE FROM maclar;SELECT COUNT(*) AS kalan FROM maclar;ROLLBACK;
kalan
0

Düzenleyici her komutun sonucunu ayrı bir sekmede gösterir; SELECT'inkine bak: kalan sıfır. DELETE FROM maclar; geçerli bir komut, "maclar tablosunun satırlarını sil" diyor ve bir satırı bile ayırmıyor. Postgres ne sordu ne uyardı. Silmeyi ROLLBACK geri aldı:

SQL
SELECT COUNT(*) AS kalan FROM maclar;
kalan
21

Bugün eklediğin maç dahil 21 maç yerinde. BEGIN ile ROLLBACK olmasaydı hepsi gitmişti. maclar.sql ilk 20 maçı geri getirirdi ama bugünkü 21. maçı ve az önce düzelttiğin skoru getiremezdi; o betik yalnız başlangıç verisini taşıyor. Gerçek bir uygulamanın tablolarında böyle bir betik de yoktur; tek çare dersin sonunda göreceğin yedek olur.

Aynı tuzak UPDATE'te de var ve daha sinsi: satırlar yerinde durduğu için bir şeyin bozulduğunu geç fark edersin.

UPDATE hesaplar SET altin = 0 WHERE ad = 'Teemo'; UPDATE hesaplar SET altin = 0; WHERE yok ad altin ad altin Ahri700 Lux450 Garen220 Jinx800 Teemo100 → 0 Ahri700 → 0 Lux450 → 0 Garen220 → 0 Jinx800 → 0 Teemo100 → 0 1 satır değişti 5 satırın hepsi değişti
Şekil 35.1 WHERE hangi satırların değişeceğini seçer. Yazılmazsa komut tablodaki bütün satırlara uygulanır.

Sağdaki komutu yine güvenlik ağının içinde dene:

SQL
BEGIN;UPDATE hesaplar SET altin = 0;SELECT ad, altin FROM hesaplar ORDER BY id;ROLLBACK;
adaltin
Ahri0
Lux0
Garen0
Jinx0
Teemo0

Herkesin altını sıfır. Bunu bir oyunun canlı veritabanında, geri dönüşsüz yaptığını düşün: bütün oyuncuların birikimi tek satırla gider. ROLLBACK yine kurtardı:

SQL
SELECT ad, altin FROM hesaplar ORDER BY id;
adaltin
Ahri700
Lux450
Garen220
Jinx800
Teemo100

Kısıtlar: bozuk veriyi kapıda durdurmak

Kuralsız tablonun nelere izin verdiğine bak:

SQL
INSERT INTO hesaplar (ad, altin, seviye)VALUES    (NULL, -500, 0),    ('Lux', 999999, 1)RETURNING id, ad, altin, seviye;
idadaltinseviye
7NULL-5000
8Lux9999991

Adı olmayan bir hesap, eksi beş yüz altın, sıfırıncı seviye ve ikinci bir Lux. Postgres hiçbirine itiraz etmedi, çünkü ona hiçbir kural söylemedin. Böyle satırlar API'ndeki bir hatayla, bir formdan ya da senin elinle girebilir; bir kez girdikten sonra her sorgunun sonucunu sessizce bozar. Çözüm, kuralları verinin durduğu yere, veritabanına yazmak. Ders 33'te bu kurallara kısıt dendiğini duymuş ve en önemlisini, birincil anahtarı kurmuştun; ötekiler bu bölümde.

JavaScript'teki doğrulamayı (Ders 13'teki girdi doğrulama, Ders 28'deki 400 yanıtı) bırakmıyorsun; ikisi birlikte çalışır. API kullanıcıya anlaşılır bir mesaj verebilmek için önce kendisi bakar. Veritabanı ise son kapıdır: API'deki bir hata da elle yazılmış bir komut da onu geçemez.

Kurallı bir tablo tasarlamak

Dükkândaki eşyalar için baştan kurallı bir tablo kur:

SQL
CREATE TABLE dukkan (    id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    ad varchar(30) NOT NULL UNIQUE CHECK (ad <> ''),    fiyat integer NOT NULL CHECK (fiyat > 0),    stok integer NOT NULL DEFAULT 0 CHECK (stok >= 0));

Kurallar sütunun tipinden sonra, boşlukla ayrılarak yazılır; bir sütuna birden çok kural koyabilirsin. Tek tek:

YazımNe der?
varchar(30)Ders 33'teki gibi en fazla 30 karakterlik metin. Bir tip, ama sınır koyduğu için kural gibi çalışır.
NOT NULLBu sütun boş (NULL) kalamaz.
UNIQUEBu sütunda aynı değer iki kez olamaz.
CHECK (ad <> '')Ad boş metin ('') olamaz. Ders 33'te boş metnin NULL sayılmadığını, bu yüzden birincil anahtardan bile geçtiğini görmüştün.
CHECK (fiyat > 0)Parantezdeki koşulu yanlış çıkan satır giremez: fiyat 0 ya da eksi olamaz.
DEFAULT 0Sütun yazılmazsa buraya 0 konur.

Birincil anahtar (PRIMARY KEY) da bir kısıttır: NOT NULL ile UNIQUE'in toplamı. Ders 33'ten beri her tabloda kullanıyorsun. Şimdi tabloyu doldur:

SQL
INSERT INTO dukkan (ad, fiyat, stok)VALUES    ('Uzun Kılıç', 350, 10),    ('Can İksiri', 50, 99),    ('Sonsuzluk Kılıcı', 3450, 2)RETURNING id, ad, stok;
idadstok
1Uzun Kılıç10
2Can İksiri99
3Sonsuzluk Kılıcı2

Şimdi kuralları tek tek çiğne ve mesajları oku. Her mesaj üç şey söyler: hangi tablo, hangi kural, hangi değer.

NOT NULL: boş geçilemez

Sık hatanull value in column "ad" of relation "dukkan" violates not-null constraint

Ne zaman çıkar? NOT NULL bir sütuna NULL yazınca ya da o sütunu INSERT'te hiç yazmayınca (ve sütunun DEFAULT'u da yoksa).

SQL
INSERT INTO dukkan (ad, fiyat)VALUES (NULL, 300);
Hata çıktısı
ERROR:  null value in column "ad" of relation "dukkan" violates not-null constraintDETAIL:  Failing row contains (4, null, 300, 0).

Ders 33'te birincil anahtarı boş bırakınca bu mesajı görmüştün; bu kez kuralı sen koydun. Parça parça oku: null value in column "ad" ("ad sütununda boş değer"), of relation "dukkan" ("dukkan tablosunda"; relation Postgres'in tablo için kullandığı sözcük), violates not-null constraint ("boş olamaz kuralını çiğniyor"). DETAIL satırı reddedilen satırı sütun sırasıyla gösterir: id 4, ad boş, fiyat 300 ve DEFAULT'tan gelen stok 0.

Nasıl düzelir? Sütuna bir değer ver. Sütunun gerçekten boş kalabilmesi gerekiyorsa tasarımı gözden geçir; belki NOT NULL yerine bir DEFAULT gerekiyordur.

UNIQUE: aynısından ikincisi olmaz

Sık hataduplicate key value violates unique constraint "dukkan_ad_key"

Ne zaman çıkar? UNIQUE (ya da birincil anahtar) olan bir sütuna, tabloda zaten bulunan bir değeri yazınca.

SQL
INSERT INTO dukkan (ad, fiyat)VALUES ('Can İksiri', 40);
Hata çıktısı
ERROR:  duplicate key value violates unique constraint "dukkan_ad_key"DETAIL:  Key (ad)=(Can İksiri) already exists.

Mesaj Ders 33'te birincil anahtarda gördüğünün aynısı; orada kuralın adı …_pkey idi. Bu kez adı Postgres tablo ve sütun adından üretti: dukkan_ad_key. DETAIL hangi değerin zaten var olduğunu söyler.

Nasıl düzelir? Başka bir ad seç ya da var olan satırı UPDATE ile değiştir. UNIQUE büyük-küçük harfe duyarlıdır: 'can iksiri' ayrı bir değer sayılır ve kabul edilir. Ders 37'de API'n bu hatayı yakalayıp kullanıcıya 409 Conflict döndürecek.

CHECK: kuralı sen yazarsın

CHECK'in parantezine WHERE'e yazdığın türden bir koşul yazarsın. Koşul yanlış çıkarsa satır giremez. Kural yalnız eklemede değil, her değişiklikte denetlenir.

Sık hatanew row for relation "dukkan" violates check constraint "dukkan_fiyat_check"

Ne zaman çıkar? Bir satırı CHECK koşulunu sağlamayan bir değerle eklemek ya da güncellemek isteyince.

SQL
INSERT INTO dukkan (ad, fiyat)VALUES ('Bedava Kılıç', 0);
Hata çıktısı
ERROR:  new row for relation "dukkan" violates check constraint "dukkan_fiyat_check"DETAIL:  Failing row contains (6, Bedava Kılıç, 0, 0).

fiyat > 0 koşulu 0 için yanlış: sınırın tam kendisi reddedildi. Aynı denetim UPDATE'te de çalışır. Sonsuzluk Kılıcı'ndan 2 tane var; 3 tane satmaya kalkarsan stok eksiye düşer:

SQL
UPDATE dukkanSET stok = stok - 3WHERE ad = 'Sonsuzluk Kılıcı';
Hata çıktısı
ERROR:  new row for relation "dukkan" violates check constraint "dukkan_stok_check"DETAIL:  Failing row contains (3, Sonsuzluk Kılıcı, 3450, -1).

new row ("yeni satır") UPDATE'te de geçer: Postgres için güncellenmiş satır da yeni bir satırdır.

Nasıl düzelir? Kuralın adı hangi sütunu denetlediğini söyler: dukkan_stok_check → stok. Değeri düzelt. Değer doğru da kural yanlışsa kuralı değiştirmen gerekir; tablo değiştirmeyi birazdan göreceksin.

Boş ad kuralı da aynı mesajla durdurur; bu kez kuralın adı dukkan_ad_check:

SQL
INSERT INTO dukkan (ad, fiyat)VALUES ('', 100);
Hata çıktısı
ERROR:  new row for relation "dukkan" violates check constraint "dukkan_ad_check"DETAIL:  Failing row contains (7, , 100, 0).

DETAIL'deki iki virgülün arası boş: reddedilen ad, boş metin. CHECK'in içine Ders 34'te gördüğün her türden koşulu yazabilirsin: <>, BETWEEN, IN, AND. Ders 33'teki tft_istatistik tablosunda ortalama sıranın 1 ile 8 arasında kalması için söz verilen kural da böyle bir şey: CHECK (ortalama_sira BETWEEN 1 AND 8).

DEFAULT: yazılmayanı doldurur

DEFAULT bir şeyi yasaklamaz; sütunu yazmadığında yerine konacak hazır değeri söyler. Ders 10'daki varsayılan parametre gibi. Çok satırlı bir INSERT'te tek bir satır için varsayılanı istiyorsan değerin yerine DEFAULT sözcüğünü yazarsın:

SQL
INSERT INTO dukkan (ad, fiyat, stok)VALUES    ('Çizme', 300, 5),    ('Kontrol Totemi', 75, DEFAULT)RETURNING id, ad, stok;
idadstok
8Çizme5
9Kontrol Totemi0

Yeni satırlar 8 ve 9 numarayı aldı; 4, 5, 6 ve 7 nerede? Reddedilen satırlar da numara harcadı: DETAIL satırlarında 4'ü, 6'yı ve 7'yi gördün; 5'i de UNIQUE'e takılan ikinci Can İksiri harcadı, onun mesajı numarayı göstermiyordu. Ders 33'te dediğimiz gibi id bir sıra sayısı değil, kimliktir; aradaki boşluklar normaldir, doldurmaya çalışma.

Uzunluk sınırı: varchar(n)

Sık hatavalue too long for type character varying(30)

Ne zaman çıkar? varchar(30) bir sütuna 30 karakterden uzun bir metin yazınca. Ders 33'te varchar(16) ile aynı mesajı görmüştün.

SQL
INSERT INTO dukkan (ad, fiyat)VALUES ('Kadim Ejderhanın Alevli Kılıcı ve Kalkanı', 4000);
Hata çıktısı
ERROR:  value too long for type character varying(30)

Bu ad 41 karakter. Sınırın tam kendisi serbest: 'Kadim Ejderhanın Alevli Kılıcı' tam 30 karakterdir ve kabul edilir. Türkçe harfler de birer karakter sayılır, bayt değil.

Nasıl düzelir? Metni kısalt ya da sınır gerçekten darsa sütunun tipini değiştir.

Kurallar hangi sırayla denetlenir?

Bir satır birden çok kuralı birden çiğniyorsa Postgres yalnız ilk takıldığı kuralı söyler; Ders 1'de Node'un yalnız ilk hatayı söylemesi gibi. Satır kapılara gelmeden önce DEFAULT'lar boşlukları doldurur, sonra sıra hep aynıdır:

Örnek satır: ('Bedava Kılıç', 0) Yeni satır tip ve uzunluk NOT NULL CHECK UNIQUE Tablo ulaşamadı 1 2 3 4 ✓ ✓ ✗ bakılmadı Reddedildi: tabloya hiçbir şey yazılmaz 1 invalid input syntax … / value too long … 2 null value in column … violates not-null constraint 3 new row for relation … violates check constraint … 4 duplicate key value violates unique constraint …
Şekil 35.2 Yeni ya da değişen her satır tabloya yazılmadan önce bu kapılardan sırayla geçer. İlk takıldığı kapının hatası gelir, gerisine hiç bakılmaz.

Örneğin ('Can İksiri', 0) hem UNIQUE'i hem CHECK'i çiğner; gelen mesaj CHECK'inkidir, çünkü o kapı önce gelir. Fiyatı düzeltip yeniden çalıştırınca sıradaki kapının, UNIQUE'in mesajını görürsün.

ALTER TABLE: dolu tabloyu yerinde değiştirmek

dukkan'ı baştan doğru kurdun. hesaplar ise hâlâ kuralsız ve içinde çöp var. Tabloyu silip yeniden kurmak içindeki bütün hesapları silmek demek; canlı bir uygulamada bu olmaz. ALTER TABLE tablonun yapısını satırlara dokunmadan değiştirir.

Önce veri, sonra kural

hesaplar'a eksi altını yasaklayan kuralı eklemeyi dene:

SQL
ALTER TABLE hesaplar ADD CHECK (altin >= 0);
Hata çıktısı
ERROR:  check constraint "hesaplar_altin_check" of relation "hesaplar" is violated by some row

Postgres kuralı eklemeden önce var olan bütün satırlara bakar. Kurala uymayan tek bir satır bile varsa ("is violated by some row": "bazı satırlar bu kuralı çiğniyor") kuralı eklemez. Önce çöpü bul; Ders 34'ün araçları burada iş görür:

SQL
SELECT id, ad, altin, seviyeFROM hesaplarWHERE ad IS NULL OR altin < 0 OR seviye < 1;
idadaltinseviye
7NULL-5000

Aynı adla iki hesap var mı? GROUP BY ve HAVING ile say:

SQL
SELECT ad, COUNT(*) AS kac_taneFROM hesaplarGROUP BY adHAVING COUNT(*) > 1;
adkac_tane
Lux2
SQL
SELECT id, ad, altin, seviyeFROM hesaplarWHERE ad = 'Lux'ORDER BY id;
idadaltinseviye
2Lux4503
8Lux9999991

Gerçek Lux 2 numara; 8 numara az önce kuralsız tabloya sızan sahte kayıt. Silinecek iki satır belli: 7 ve 8. Birincil anahtarla hedef almak en güvenlisi, çünkü id iki satırda aynı olamaz:

SQL
DELETE FROM hesaplarWHERE id IN (7, 8)RETURNING id, ad;
idad
7NULL
8Lux

Şimdi kurallar:

SQL
ALTER TABLE hesaplar ALTER COLUMN ad SET NOT NULL;ALTER TABLE hesaplar ADD UNIQUE (ad);ALTER TABLE hesaplar ADD CHECK (altin >= 0);

Üçü de hatasız çalıştı: ad boş geçilemez oldu, aynı ad yasaklandı, eksi altın yasaklandı. ALTER COLUMN … SET … var olan bir sütunun bir özelliğini değiştirir; ADD … tabloya yeni bir şey ekler.

Birden çok değişikliği tek ALTER TABLE'da virgülle ayırarak da yapabilirsin. Altın ve seviyeyi boş geçilemez yap ve ikisine varsayılan değer ver:

SQL
ALTER TABLE hesaplar    ALTER COLUMN altin SET NOT NULL,    ALTER COLUMN altin SET DEFAULT 500,    ALTER COLUMN seviye SET NOT NULL,    ALTER COLUMN seviye SET DEFAULT 1;

Dene: yalnız adla yeni bir hesap aç.

SQL
INSERT INTO hesaplar (ad)VALUES ('Annie')RETURNING id, ad, altin, seviye;
idadaltinseviye
9Annie5001

Yasuo'nun başına gelen bir daha olmaz: NULL yerine varsayılanlar geldi.

Sütun eklemek

SQL
ALTER TABLE hesaplar ADD COLUMN unvan text;SELECT id, ad, unvan FROM hesaplar ORDER BY id;
idadunvan
1AhriNULL
2LuxNULL
3GarenNULL
4JinxNULL
5TeemoNULL
9AnnieNULL

Var olan satırlar yeni sütunda NULL aldı. Varsayılanı olan bir sütun eklersen var olan satırlar da o değeri alır:

SQL
ALTER TABLE hesaplar ADD COLUMN aktif boolean NOT NULL DEFAULT true;SELECT ad, aktif FROM hesaplar ORDER BY id;
adaktif
Ahritrue
Luxtrue
Garentrue
Jinxtrue
Teemotrue
Annietrue
Sık hatacolumn "eposta" of relation "hesaplar" contains null values

Ne zaman çıkar? Dolu bir tabloya varsayılansız bir NOT NULL sütun eklerken ya da içinde NULL bulunan bir sütuna SET NOT NULL derken.

SQL
ALTER TABLE hesaplar ADD COLUMN eposta text NOT NULL;
Hata çıktısı
ERROR:  column "eposta" of relation "hesaplar" contains null values

Yeni sütun var olan altı satıra ne koyacağını bilmiyor. Koyabileceği tek şey NULL ve kural buna izin vermiyor.

Nasıl düzelir? Üç adımda git: sütunu kuralsız ekle, UPDATE ile her satıra gerçek bir değer yaz, en son ALTER COLUMN eposta SET NOT NULL de. UNIQUE eklerken aynı türden bir engele takılırsan mesaj could not create unique index "…" olur; DETAIL hangi değerin tekrarlandığını söyler (Key (ad)=(Lux) is duplicated.).

Ad değiştirmek ve sütun silmek

SQL
ALTER TABLE hesaplar RENAME COLUMN unvan TO lakap;

Veriler yerinde kalır, yalnız sütunun adı değişir. Ama sütunu eski adıyla kullanan her sorgu artık bozuktur:

SQL
SELECT ad, unvan FROM hesaplar;
Hata çıktısı
ERROR:  column "unvan" does not exist

Ders 37'de API'n SQL'i kodun içinden gönderecek. Bir sütunun adını değiştirdiğinde onu kullanan her satırı da değiştirmen gerekir; ad değiştirmek ucuz görünür ama zinciri uzundur. Kullanmadığın bir sütunu silmek için:

SQL
ALTER TABLE hesaplar DROP COLUMN lakap;SELECT * FROM hesaplar ORDER BY id;
idadaltinseviyeaktif
1Ahri7001true
2Lux4503true
3Garen2203true
4Jinx8005true
5Teemo1001true
9Annie5001true

DROP COLUMN sütunu içindeki bütün verilerle birlikte siler ve burada da "emin misin?" sorusu yok. Sonuçtaki aktif sütununa da bak: Ders 34'te SELECT *'ın tablo değişince sürpriz yaptığını söylemiştik. * artık bu sütunu da getiriyor; sütun adlarını tek tek yazan bir sorgu hiç etkilenmezdi. Gördüğün kalıpların özeti:

Ne yapmak istiyorsun?Kalıp
Sütun eklemekALTER TABLE t ADD COLUMN sutun tip;
Sütun silmekALTER TABLE t DROP COLUMN sutun;
Sütunun adını değiştirmekALTER TABLE t RENAME COLUMN eski TO yeni;
Tablonun adını değiştirmekALTER TABLE t RENAME TO yeni_ad;
Boş geçilemez yapmakALTER TABLE t ALTER COLUMN sutun SET NOT NULL;
Varsayılan vermekALTER TABLE t ALTER COLUMN sutun SET DEFAULT deger;
Kural eklemekALTER TABLE t ADD CHECK (kosul); ya da ADD UNIQUE (sutun);

Transaction: ya hep ya hiç

Oyuncular birbirine altın gönderebilsin. Lux, Teemo'ya 500 altın gönderiyor: Teemo'nun altını artmalı, Lux'unki azalmalı. İki komut. Önce ikisini ayrı ayrı, birini çalıştırıp sonra ötekini yazarak dene:

SQL
UPDATE hesaplarSET altin = altin + 500WHERE ad = 'Teemo'RETURNING ad, altin;
adaltin
Teemo600
SQL
UPDATE hesaplarSET altin = altin - 500WHERE ad = 'Lux';
Hata çıktısı
ERROR:  new row for relation "hesaplar" violates check constraint "hesaplar_altin_check"DETAIL:  Failing row contains (2, Lux, -50, 3, t).

Lux'un 450 altını var; 500 düşünce -50 olurdu ve az önce eklediğin kural bunu durdurdu. (DETAIL'deki t, true'nun kısaltması: aktif sütunu.) Kural işini yaptı. Peki sonuç?

SQL
SELECT ad, altinFROM hesaplarWHERE ad IN ('Lux', 'Teemo')ORDER BY id;
adaltin
Lux450
Teemo600

Teemo 500 altın kazandı, Lux hiçbir şey kaybetmedi: 500 altın yoktan var oldu. Oyunlarda buna kopyalama açığı (dupe) denir ve oyunun ekonomisini çökertir. Sorun kuralda değil: iki adımlı bir işin yarısı oldu, yarısı olmadı. Önce Teemo'nun hak etmediği altını geri al:

SQL
UPDATE hesaplarSET altin = altin - 500WHERE ad = 'Teemo'RETURNING ad, altin;
adaltin
Teemo100

BEGIN, COMMIT, ROLLBACK

Jinx, Teemo'ya 200 altın gönderiyor. Bu kez iki komutu BEGIN ile COMMIT arasına al ve bloğun tamamını tek seferde çalıştır:

SQL
BEGIN;UPDATE hesaplar SET altin = altin + 200 WHERE ad = 'Teemo';UPDATE hesaplar SET altin = altin - 200 WHERE ad = 'Jinx';COMMIT;SELECT ad, altin FROM hesaplar WHERE ad IN ('Jinx', 'Teemo') ORDER BY id;
adaltin
Jinx600
Teemo300

BEGIN'den sonraki değişiklikler önce geçicidir; transaction'ın dışından bakan biri onları görmez. COMMIT gelince ikisi birlikte kalıcı olur ve herkes görür. Şimdi parası yetmeyen Lux'un transferini aynı kalıpla dene:

SQL
BEGIN;UPDATE hesaplar SET altin = altin + 500 WHERE ad = 'Teemo';UPDATE hesaplar SET altin = altin - 500 WHERE ad = 'Lux';COMMIT;
Hata çıktısı
ERROR:  new row for relation "hesaplar" violates check constraint "hesaplar_altin_check"DETAIL:  Failing row contains (2, Lux, -50, 3, t).

Aynı hata, ama bu kez arkasında yarım bir iş kalmadı. İkinci komut hata verince transaction bozuldu: Postgres aynı çalıştırmadaki sonraki komutları, COMMIT dahil, atladı. Bozuk bir transaction'da artık tek bir iş yapılabilir: onu kapatmak.

Sık hatacurrent transaction is aborted, commands ignored until end of transaction block

Ne zaman çıkar? Transaction içindeki bir komut hata verdikten sonra, ROLLBACK demeden aynı bağlantıdan yeni bir komut gönderince.

SQL
SELECT ad, altin FROM hesaplar;
Hata çıktısı
ERROR:  current transaction is aborted, commands ignored until end of transaction block

"Şu anki transaction iptal edildi; blok bitene kadar komutlar yok sayılıyor." Postgres yarım kalmış bir işin üstüne yeni iş koymaz. Asıl hata bu değil, yukarıdaki ilk kırmızı mesaj.

Nasıl düzelir? ROLLBACK; çalıştır, ilk hatanın sebebini düzelt ve transaction'ı baştan gönder. Bu mesajı en çok bağlantıyı açık tutan araçlarda görürsün: komut satırındaki psql ya da Ders 37'de yazacağın Node programı. Düzenleyicin her çalıştırmayı ayrı bağlantıyla yapıyorsa Postgres yarım transaction'ı bağlantı kapanırken zaten geri almıştır; o zaman bu sorgu normal çalışır, arkasından gelen ROLLBACK da yalnız WARNING: there is no transaction in progress ("devam eden bir transaction yok") uyarısı verir. İki durumda da verin güvende.

Transaction'ı kapat ve sonuca bak:

SQL
ROLLBACK;SELECT ad, altin FROM hesaplar WHERE ad IN ('Lux', 'Teemo') ORDER BY id;
adaltin
Lux450
Teemo300

Teemo'nun +500'ü de geri alındı. Ya ikisi birden ya hiçbiri: bu kez altın yoktan var olmadı.

Transaction olmadan Başlangıç Lux450 Teemo100 1. adım ✓ Teemo +500 Teemo artık 600 2. adım ✗ Lux -500 CHECK reddetti Sonuç Lux450 Teemo600 500 altın yoktan var oldu Transaction ile BEGIN Lux450 Teemo300 1. adım ✓ Teemo +500 henüz geçici 2. adım ✗ Lux -500 → ROLLBACK Sonuç Lux450 Teemo300 Hiçbir şey değişmedi
Şekil 35.3 Lux'un parası yetmeyen transferini iki kez denedin. Üstte transaction yoktu: ilk adım kaldı, altın yoktan var oldu. Altta transaction vardı: ikinci adım düşünce ilk adım da geri alındı.

Bu davranışı Ders 29'dan tanıyorsun: atomik işlem, yani ya bütünüyle olan ya hiç olmayan iş. Orada tek bir dosyayı rename ile atomik yazmıştın; transaction aynı sözü birden çok SQL komutu için verir. Veritabanı kitaplarında bu özelliğe atomiklik (atomicity) denir ve transaction'ın verdiği ilk sözdür.

Ne zaman transaction kullanırsın?

  • Birbirine bağlı iki ya da daha çok değişiklikte: transfer, sipariş verip stoktan düşmek, bir hesabı ve ona ait kayıtları birlikte silmek.
  • Riskli bir değişikliği kalıcı yapmadan önce görmek istediğinde: BEGIN, değişiklik, SELECT ile bak, sonra COMMIT ya da ROLLBACK.
  • Tek bir komut için gerekmez; tek komut zaten kendi başına ya hep ya hiç çalışır. Bunu çok satırlı INSERT'te gördün.

Yedek almak

Transaction seni COMMIT'e kadar korur. COMMIT'ten sonra geri alma yoktur: WHERE'siz bir UPDATE'i onaylarsan, API'ndeki bir hata satırları silerse ya da yanlış sütunu düşürürsen tek dönüş yolun yedektir. Ders 33'ten hatırla: Git klasördeki dosyaları kaydeder, veritabanındaki satırları değil. schema.sql ve maclar.sql tabloyu baştan kurmanın tarifi; ama bugün eklediğin 21. maç, düzelttiğin skor, açılan hesaplar onlarda yok. O anki verinin kendisini saklayan ayrı bir kayda ihtiyacın var.

Elinde dört yol var: üçü Neon'un içinde, biri kendi bilgisayarında.

YolNerede durur?Ne zaman?Neon Free planda
Geçmişten geri yüklemeNeon"Az önce bir şeyi bozdum."son 6 saat
SnapshotNeonBüyük bir değişiklikten hemen önceaynı anda 1 tane
Dal (branch)NeonRiskli bir komutu önce kopyada denemekprojede 10 dal
pg_dump dosyasıSenin bilgisayarınDüzenli, Neon'dan bağımsız kopyasınır senin diskin

Sayılar Ekim 2026'daki Neon fiyat sayfasından; planlar değişebilir. Aşağıdaki düğme adları da o tarihteki arayüzden, arayüz değişmiş olabilir.

Geçmişten geri yükleme

Neon, veritabanındaki değişikliklerin kaydını bir süre tutar. Bu süreye geçmiş penceresi (history window) denir ve Free planda 6 saattir. Pencerenin içindeki herhangi bir ana dönebilirsin:

  1. Neon'da projeni aç, sol menüde Postgres database altındaki Backup & Restore sayfasına gir.
  2. Restore from history'ye tıkla. From history sekmesinde dönmek istediğin tarihi ve saati seç, Next'e bas.
  3. Özeti oku ve Restore'a bas.

Neon geri yüklemeden önceki hâli de atmaz; production_old_… gibi adlı bir yedek dalda saklar. Yanlış saati seçtiysen oradan dönebilirsin. Geri yükleme bütün dalı o ana götürür: o saatten sonra eklenen doğru satırlar da gider. Bu yüzden saati dikkatle seç.

Snapshot: elle kayıt noktası

Büyük bir değişiklikten önce (bir sütunu silmek, binlerce satırı güncellemek) elle bir snapshot al: veritabanının o anki fotoğrafı. Aynı Backup & Restore sayfasındaki Create snapshot düğmesiyle alınır. Free planda aynı anda bir snapshot tutabilirsin. Snapshot'tan geri yüklediğinde de Neon önceki hâli ayrı bir dalda saklar.

Dal: antrenman kopyası

Ders 33'te SQL Editor'ün üstündeki seçicide gördüğün production, projenin asıl dalıdır. Branches sayfasında New branch'e basıp kaynak olarak production'ı ve Current data seçeneğini seçersen o anki verinin bir kopyası oluşur. Sorguyu hangi dalda çalıştıracağını yine o seçiciden seçersin. Neon yeni dalı varsayılan olarak bir gün sonra kendiliğinden siler (Automatically delete branch after); deneme dalları için bu iyi bir ayar. Git'in de dalları var; onları Ders 52'de göreceksin.

pg_dump: kendi bilgisayarında bir kopya

İlk üç yolun ortak bir zayıflığı var: hepsi Neon'un içinde. Hesabına erişemezsen, projeyi yanlışlıkla silersen ya da başka bir sağlayıcıya taşınmak istersen elinde bağımsız bir kopya olmalı. PostgreSQL'in kendi aracı pg_dump, veritabanını tabloları, kuralları ve satırlarıyla birlikte bir SQL dosyasına döker.

pg_dump, Node gibi bilgisayarına kurman gereken ayrı bir programdır. Windows'ta resmî PostgreSQL yükleyicisiyle gelir:

  1. postgresql.org/download/windows sayfasından yükleyiciyi indir (yükleyiciyi EDB hazırlıyor). Sürüm, Neon projendeki Postgres sürümüyle aynı ya da ondan yeni olmalı: pg_dump kendi sürümünden eski bir Postgres'i yedekleyebilir, yenisini yedekleyemez. Projendeki sürümü görmek için SQL Editor'de SHOW server_version; çalıştır. En kolayı en yeni sürümü seçmek (Ekim 2026'da 18).
  2. Select Components adımında yalnız Command Line Tools'u işaretli bırak. Sunucuya (PostgreSQL Server) ihtiyacın yok; veritabanın Neon'da.
  3. Kurulum bitince araçlar C:\Program Files\PostgreSQL\18\bin klasörüne gelir (18 yerine seçtiğin sürüm).

Kurulumdan sonra terminale pg_dump --version yazarsan büyük olasılıkla şunu görürsün:

Terminal — PowerShell
PS C:\kod\veritabani> pg_dump --versionpg_dump : The term 'pg_dump' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.At line:1 char:1+ pg_dump --version+ ~~~~~~~    + CategoryInfo          : ObjectNotFound: (pg_dump:String) [], CommandNotFoundException    + FullyQualifiedErrorId : CommandNotFoundException

Ders 0'daki tanıdık mesaj: yükleyici bin klasörünü PATH'e eklemedi, PowerShell pg_dump'ı bulamıyor. Türkçe Windows'ta da bu mesajı büyük olasılıkla İngilizce görürsün. PATH'le uğraşmak yerine programı tam yoluyla çağırabilirsin. Yolda boşluk olduğu için tırnak, tırnaklı bir yolu çalıştırmak için de başına & gerekir:

PowerShell
& "C:\Program Files\PostgreSQL\18\bin\pg_dump.exe" --version

Bu kez bir sürüm numarası yazmalı.

Yedek dosyası terminalin bulunduğu klasöre, C:\kod\veritabani'ye yazılacak. Bu klasör bir Git deposu ve yedekte bütün verin olacak. Yanlışlıkla bir commit'e girmesin diye önce bu klasörde, Ders 30'daki gibi, .gitignore adlı bir dosya aç ve içine tek satır yaz:

.gitignore
yedek.sql

Bağlantı dizesini Neon'dan al: Connect düğmesine bas, Connection pooling seçeneğini kapat ve dizeyi kopyala. Adreste -pooler geçmemeli; Neon, pg_dump'ın havuzlu bağlantıyla çalıştırılmamasını söylüyor. Sonra yedeği al (dizenin yerine kendi dizeni yapıştır):

PowerShell
& "C:\Program Files\PostgreSQL\18\bin\pg_dump.exe" --dbname="postgresql://neondb_owner:SIFREN@ep-ornek-123456.eu-central-1.aws.neon.tech/neondb?sslmode=require&channel_binding=require" --no-owner --inserts --file=yedek.sql
SeçenekNe yapar?
--dbname="…"Hangi veritabanı: bağlantı dizesi.
--no-ownerTabloların sahibini (Neon'daki kullanıcı adını) dosyaya yazmaz; dosya başka bir veritabanına sorunsuz yüklenir.
--insertsSatırları INSERT komutları olarak yazar. Dosya okunaklı olur; çok büyük veritabanlarında geri yükleme yavaşlar.
--file=yedek.sqlÇıktının gideceği dosya.

Komut başarılı olunca hiçbir şey yazmaz; klasörde yedek.sql belirir. Aç ve bak: yedek dediğin, veritabanını baştan kuran SQL komutlarından ibaret. Aşağıdaki satırlar bu dersteki hesaplar tablosunu (Postgres 18 ile) dökerek aldığımız dosyadan; senin dosyanda satır numaraları ve tablolar farklı olacak:

yedek.sql
CREATE TABLE public.hesaplar (    id integer NOT NULL,    ad text NOT NULL,    altin integer DEFAULT 500 NOT NULL,    seviye integer DEFAULT 1 NOT NULL,    aktif boolean DEFAULT true NOT NULL,    CONSTRAINT hesaplar_altin_check CHECK ((altin >= 0)));
yedek.sql
INSERT INTO public.hesaplar OVERRIDING SYSTEM VALUE VALUES (2, 'Lux', 450, 3, true);INSERT INTO public.hesaplar OVERRIDING SYSTEM VALUE VALUES (3, 'Garen', 220, 3, true);INSERT INTO public.hesaplar OVERRIDING SYSTEM VALUE VALUES (1, 'Ahri', 700, 1, true);INSERT INTO public.hesaplar OVERRIDING SYSTEM VALUE VALUES (9, 'Annie', 500, 1, true);INSERT INTO public.hesaplar OVERRIDING SYSTEM VALUE VALUES (5, 'Teemo', 300, 1, true);INSERT INTO public.hesaplar OVERRIDING SYSTEM VALUE VALUES (4, 'Jinx', 600, 5, true);
  • CREATE TABLE bu derste koyduğun kuralları taşıyor: NOT NULL, DEFAULT, CHECK. UNIQUE ve birincil anahtar dosyanın sonlarına doğru ayrı ALTER TABLE satırlarıyla eklenir.
  • OVERRIDING SYSTEM VALUE "id'yi kendin verme, dosyadakini kullan" demek; geri yüklenen her hesap eski numarasını korur. Ders 33'te kimlik sütununa elle değer yazınca aldığın HINT'i hatırla: bu yol, kayıtları bir veritabanından ötekine taşırken kullanılır. Yedek tam olarak bunu yapıyor.
  • Satırlar id sırasıyla değil: pg_dump satırları tabloda fiziksel olarak durdukları sırayla yazar, güncellenen satırlar da yer değiştirir. Sıra önemli değil, numaralar korunuyor.
  • Dosyanın başında SET satırları ve \restrict ile başlayan bir güvenlik satırı görebilirsin. Silme; dosyayı geri yükleyecek araç onları kullanır.

Geri yüklemek için aynı klasördeki psql aracını kullanırsın; dosyadaki komutları sırayla çalıştırır. Yedeği boş bir veritabanına yükle. Dolu bir veritabanına yüklersen relation "hesaplar" already exists gibi hatalar alırsın.

PowerShell
& "C:\Program Files\PostgreSQL\18\bin\psql.exe" --dbname="BOŞ_VERİTABANININ_DİZESİ" --file=yedek.sql

Sık hatalar

Bu derste karşına çıkabilecek öteki hatalar. Bütün derslerin hataları hata rehberinde toplanıyor.

Sık hatacheck constraint "hesaplar_altin_check" of relation "hesaplar" is violated by some row

Ne zaman çıkar? Dolu bir tabloya ALTER TABLE … ADD CHECK ile kural eklerken, tabloda kurala uymayan en az bir satır varsa. ALTER TABLE bölümünde eksi altınlı hesap yüzünden gördün.

Nasıl düzelir? Kuralın tersini WHERE'e yazıp kuralı çiğneyen satırları bul (WHERE altin < 0), onları UPDATE ile düzelt ya da DELETE ile sil, sonra kuralı yeniden ekle. SET NOT NULL ve ADD UNIQUE da aynı şekilde önce eski satırlara bakar.

Sık hataKomut hatasız bitti ama hiçbir şey değişmedi

Ne zaman çıkar? UPDATE ya da DELETE'in WHERE'i hiçbir satırı tutmayınca: 'Ahri' yerine 'ahri' yazmak, fazladan bir boşluk, yanlış bir id. Postgres bunu hata saymaz; "sıfır satırı değiştirdim" der ve geçer.

Nasıl düzelir? Komuta RETURNING ekle ya da önce aynı WHERE ile SELECT çalıştır. Boş sonuç geliyorsa sorun WHERE'de: değeri tablodakiyle karşılaştır, büyük-küçük harfe bak.

Sık hataThe '--' operator works only on variables or on properties.

Ne zaman çıkar? Tırnaklı bir program yolunu başına & koymadan çalıştırınca.

Terminal — PowerShell
PS C:\kod\veritabani> "C:\Program Files\PostgreSQL\18\bin\pg_dump.exe" --versionAt line:1 char:52+ "C:\Program Files\PostgreSQL\18\bin\pg_dump.exe" --version+                                                    ~~~~~~~Unexpected token 'version' in expression or statement.At line:1 char:1+ "C:\Program Files\PostgreSQL\18\bin\pg_dump.exe" --version+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~The '--' operator works only on variables or on properties.    + CategoryInfo          : ParserError: (:) [], ParentContainsErrorRecordException    + FullyQualifiedErrorId : UnexpectedToken

PowerShell tırnak içindeki yolu bir metin sandı; metinden sonra gelen --version'ı da bir işlem işareti (--) ile bir sözcük sanıp anlayamadı.

Nasıl düzelir? Satırın başına & koy: & "C:\…\pg_dump.exe" --version. & "tırnak içindeki şey bir program, onu çalıştır" der. & varken The term 'C:\Program Files\PostgreSQL\18\bin\pg_dump.exe' is not recognized alırsan yol yanlıştır; çoğu zaman klasördeki sürüm numarası (18) sende farklıdır. Dosya Gezgini'nde C:\Program Files\PostgreSQL'e bakıp doğru numarayı yaz.

Görevler

Görevlerde yeni bir tablo kullanıyorsun: kadro, takımındaki oyuncular ve canları (hp). Görevleri sırayla yap; her görev bir öncekinin bıraktığı tabloyla devam eder. İşler karışırsa DROP TABLE kadro; ile tabloyu silip Görev 1'den yeniden başlayabilirsin. Önce kendin dene, takılırsan ipuçlarını sırayla aç.

Görev 1

Kadroyu kur

Önce tabloyu oluştur:

SQL
CREATE TABLE kadro (    id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    ad text NOT NULL UNIQUE,    rol text NOT NULL,    hp integer NOT NULL DEFAULT 600 CHECK (hp >= 0));

Sonra tek bir INSERT ile dört oyuncu ekle ve eklenenlerin id, ad ve hp sütunlarını geri al: Garen (üst, 690 can), Ahri (orta, 590), Jinx (alt, 630) ve Soraka (destek). Soraka'nın canını yazma; tablonun varsayılanı versin. Beklenen sonuç:

idadhp
1Garen690
2Ahri590
3Jinx630
4Soraka600

Sınır denemesi: aynı INSERT'i bir kez daha çalıştır. Ne olmalı?

İpucu 1

Çok satırlı INSERT'te satırlar virgülle ayrılır, ; yalnız en sonda. RETURNING'i ;'den önce yaz.

İpucu 2

Dört satırın da aynı sayıda değeri olmalı. Soraka'nın canının yerine DEFAULT yaz.

Çözümü göster
SQL
INSERT INTO kadro (ad, rol, hp)VALUES    ('Garen', 'üst', 690),    ('Ahri', 'orta', 590),    ('Jinx', 'alt', 630),    ('Soraka', 'destek', DEFAULT)RETURNING id, ad, hp;
idadhp
1Garen690
2Ahri590
3Jinx630
4Soraka600

Aynı komutu bir kez daha çalıştırınca UNIQUE kuralı ilk satırda durdurur ve dört satırdan hiçbiri eklenmez:

SQL
INSERT INTO kadro (ad, rol, hp)VALUES    ('Garen', 'üst', 690),    ('Ahri', 'orta', 590),    ('Jinx', 'alt', 630),    ('Soraka', 'destek', DEFAULT)RETURNING id, ad, hp;
Hata çıktısı
ERROR:  duplicate key value violates unique constraint "kadro_ad_key"DETAIL:  Key (ad)=(Garen) already exists.
Görev 2

Bot koridoru baskını

Alt koridordaki iki oyuncu, rolü alt ya da destek olanlar, baskın yedi ve 150'şer hasar aldı. Tek bir UPDATE ile ikisinin canını düşür; adlarını ve yeni canlarını geri al. Beklenen sonuç (satırların sırası sende farklı olabilir):

adhp
Jinx480
Soraka450

Sonra sınırı dene: Jinx'e önce 481 hasar daha vurmayı dene, ardından 480 vur. Hangisi kabul edilmeli, hangisi reddedilmeli?

İpucu 1

İki rolü aynı anda seçmek için Ders 34'teki IN'i kullan.

İpucu 2

SET hp = hp - 150: sağ taraf önce hesaplanır, sonuç sütuna konur.

İpucu 3

481 hasar Jinx'in canını -1'e düşürür. Tablodaki CHECK (hp >= 0) buna ne der? 480 hasar canı tam 0 yapar; 0 kurala uyuyor mu?

Çözümü göster
SQL
UPDATE kadroSET hp = hp - 150WHERE rol IN ('alt', 'destek')RETURNING ad, hp;
adhp
Jinx480
Soraka450

481 hasar reddedilir; Jinx'in canı değişmez:

SQL
UPDATE kadroSET hp = hp - 481WHERE ad = 'Jinx';
Hata çıktısı
ERROR:  new row for relation "kadro" violates check constraint "kadro_hp_check"DETAIL:  Failing row contains (3, Jinx, alt, -1).

480 hasar kabul edilir, çünkü 0 >= 0 doğrudur:

SQL
UPDATE kadroSET hp = hp - 480WHERE ad = 'Jinx'RETURNING ad, hp;
adhp
Jinx0
Görev 3

Düşenleri çıkar

Canı 0 olan oyuncular kadrodan çıkıyor. Önce SELECT ile kimlerin gideceğine bak, sonra aynı WHERE ile sil ve silinenlerin id ve ad sütunlarını RETURNING ile göster. Sınırı doğru yaz: canı 0 olan gitmeli, canı 1 olan biri olsaydı kalmalıydı. Beklenen sonuç:

idad
3Jinx
İpucu 1

Menzil göstergesi: SELECT id, ad, hp FROM kadro WHERE … ile başla.

İpucu 2

hp < 0 yazarsan kimse gelmez; CHECK zaten eksi cana izin vermiyor. Sınırın kendisi 0: hp = 0 ya da hp <= 0.

Çözümü göster
SQL
SELECT id, ad, hpFROM kadroWHERE hp = 0;
idadhp
3Jinx0

Sınırı ıskalamak böyle görünür; hata yok ama kimse gelmiyor:

SQL
SELECT id, ad, hpFROM kadroWHERE hp < 0;
idadhp

Doğru WHERE ile sil:

SQL
DELETE FROM kadroWHERE hp = 0RETURNING id, ad;
idad
3Jinx
Görev 4

Kadroya yama

kadro tablosunu ALTER TABLE ile değiştir:

  1. seviye adında bir sütun ekle: tam sayı, boş geçilemez, varsayılanı 1, değeri 1 ile 18 arasında olmalı.
  2. rol sütununun adını koridor yap.
  3. koridor yalnız 'üst', 'orman', 'orta', 'alt' ya da 'destek' olabilsin.

Üç değişiklikten sonra SELECT ad, koridor, hp, seviye FROM kadro ORDER BY id; şunu göstermeli:

adkoridorhpseviye
Garenüst6901
Ahriorta5901
Sorakadestek4501

Sonra kuralları dene. Her denemenin sonucunu önce kendin tahmin et:

  • Lux'u yalnız adı ve koridoruyla ('orta') ekle: canı ve seviyesi ne olur?
  • Yasuo'yu koridorunu 'ust' yazarak eklemeyi dene.
  • Ahri'nin seviyesini önce 18, sonra 19 yap.
İpucu 1

Üç ayrı ALTER TABLE yaz: ADD COLUMN, RENAME COLUMN, ADD CHECK.

İpucu 2

Sütunu eklerken tipten sonra bütün kurallarını sıralayabilirsin: ADD COLUMN seviye integer NOT NULL DEFAULT 1 CHECK (…). "1 ile 18 arası" için Ders 34'teki BETWEEN'i kullan; iki ucu da dahildir.

İpucu 3

Koridor kuralı bir CHECK ve içinde IN: CHECK (koridor IN (…)). Adı değiştirdikten sonra kuralda yeni adı kullan.

Çözümü göster
SQL
ALTER TABLE kadro ADD COLUMN seviye integer NOT NULL DEFAULT 1 CHECK (seviye BETWEEN 1 AND 18);ALTER TABLE kadro RENAME COLUMN rol TO koridor;ALTER TABLE kadro ADD CHECK (koridor IN ('üst', 'orman', 'orta', 'alt', 'destek'));SELECT ad, koridor, hp, seviye FROM kadro ORDER BY id;
adkoridorhpseviye
Garenüst6901
Ahriorta5901
Sorakadestek4501

Lux'un canı ve seviyesi varsayılanlardan gelir:

SQL
INSERT INTO kadro (ad, koridor)VALUES ('Lux', 'orta')RETURNING ad, koridor, hp, seviye;
adkoridorhpseviye
Luxorta6001

'ust' listede yok (ü eksik):

SQL
INSERT INTO kadro (ad, koridor)VALUES ('Yasuo', 'ust');
Hata çıktısı
ERROR:  new row for relation "kadro" violates check constraint "kadro_koridor_check"DETAIL:  Failing row contains (7, Yasuo, ust, 600, 1).

18 sınırın tam kendisi, kabul edilir; 19 reddedilir:

SQL
UPDATE kadroSET seviye = 18WHERE ad = 'Ahri'RETURNING ad, seviye;
adseviye
Ahri18
SQL
UPDATE kadroSET seviye = 19WHERE ad = 'Ahri';
Hata çıktısı
ERROR:  new row for relation "kadro" violates check constraint "kadro_seviye_check"DETAIL:  Failing row contains (2, Ahri, orta, 590, 19).

Yasuo'nun reddedilen satırı 7 numarayı harcadı; Lux 6'yı almıştı, 5'i de Görev 1'deki tekrar denemesi harcamıştı.

Görev 5

Dalda dene

WHERE'siz DELETE'i bu kez gerçekten, ama bir kopyada çalıştır. Neon'da production'dan, Current data ile deneme adında bir dal aç. SQL Editor'de dalı deneme seç ve orada DELETE FROM kadro; çalıştır, ardından satırları say. Sonra dalı yeniden production yap ve aynı sayımı orada çalıştır. En son deneme dalını sil. Beklenen: deneme dalında 0, production'da 4 (Görev 4'ün sonundaki kadro).

İpucu 1

Dalı Branches sayfasındaki New branch ile açarsın; Parent branch olarak production'ı, veri olarak Current data'yı seç.

İpucu 2

Her sorgudan önce SQL Editor'ün üst kısmında hangi dalın seçili olduğuna bak. Dalı silmek için Branches sayfasında dalı aç, üç noktalı menüden Delete'i seç.

Çözümü göster

deneme dalı seçiliyken:

SQL
DELETE FROM kadro;SELECT COUNT(*) AS kalan FROM kadro;

kalan 0 çıkar. Dalı production yapıp yalnız sayımı çalıştırınca:

SQL
SELECT COUNT(*) AS kalan FROM kadro;

kalan 4 çıkar: Garen, Ahri, Soraka ve Lux yerinde. Dal, asıl veritabanının kopyasıydı; ona ne yaparsan yap production etkilenmedi. Gerçek bir projede riskli bir tablo değişikliğini (DROP COLUMN, büyük bir UPDATE) önce böyle bir dalda dene, sonucu beğenirsen production'da çalıştır.

Görev 6

Soraka'nın bağışı

Soraka kendi canından takım arkadaşına can verebiliyor. Bir transaction içinde Soraka'nın 200 canını Garen'e aktar: önce Garen'in canını artır, sonra Soraka'nınkini azalt, COMMIT et ve ikisinin canını göster. Beklenen:

adhp
Garen890
Soraka250

Sonra aynı aktarımı 300 canla dene. Hata aldıktan sonra transaction'ı doğru kapat ve iki oyuncunun canının değişmediğini göster. Son soru: Soraka bu kurallarla en fazla kaç can aktarabilir?

İpucu 1

Kalıp: BEGIN;, iki UPDATE, COMMIT;. Hepsini tek seferde çalıştır; canları görmek için sona bir SELECT ekleyebilirsin.

İpucu 2

300'lük aktarımda ikinci UPDATE Soraka'nın canını -50 yapmak isteyecek ve CHECK reddedecek. Hata transaction'ı bozar; ROLLBACK; ile kapat.

İpucu 3

Transaction olmasaydı ne olurdu? İlk UPDATE kalırdı: Soraka'dan tek can gitmeden Garen 300 can kazanırdı.

Çözümü göster
SQL
BEGIN;UPDATE kadro SET hp = hp + 200 WHERE ad = 'Garen';UPDATE kadro SET hp = hp - 200 WHERE ad = 'Soraka';COMMIT;SELECT ad, hp FROM kadro WHERE ad IN ('Garen', 'Soraka') ORDER BY id;
adhp
Garen890
Soraka250

300'lük aktarım:

SQL
BEGIN;UPDATE kadro SET hp = hp + 300 WHERE ad = 'Garen';UPDATE kadro SET hp = hp - 300 WHERE ad = 'Soraka';COMMIT;
Hata çıktısı
ERROR:  new row for relation "kadro" violates check constraint "kadro_hp_check"DETAIL:  Failing row contains (4, Soraka, destek, -50, 1).
SQL
ROLLBACK;SELECT ad, hp FROM kadro WHERE ad IN ('Garen', 'Soraka') ORDER BY id;
adhp
Garen890
Soraka250

Garen'in +300'ü de geri alındı. Soraka en fazla 250 can aktarabilir: canı tam 0 olur ve CHECK (hp >= 0) buna izin verir; 251 reddedilir. Oyunda Soraka'nın kendini öldürmesini istemiyorsan kural hp > 0 olmalıydı, ama o zaman Görev 2'deki Jinx de 0 cana inemezdi. Kuralı seçmek, oyunun kuralını seçmektir.

Kendini sına

Soru 1

Bu komut kaç satırı değiştirir?

SQL
UPDATE hesaplar SET seviye = seviye + 1;
Cevabı göster

Tablodaki bütün satırları. WHERE yok; komut her satıra uygulanır ve Postgres ne sorar ne uyarır. Bilerek herkesi değiştirmek istiyorsan doğru komut budur, ama önce SELECT COUNT(*) ile kaç satıra dokunacağını gör.

Soru 2

Hangisi hata verir, hangisi hatasız bitip hiçbir şey değiştirmez?

SQL
UPDATE hesaplar SET altin = 0 WHERE ad = 'lux';UPDATE hesaplar SET altin = 0 WHERE adi = 'Lux';
Cevabı göster

Birincisi hatasız biter ve sıfır satır değiştirir: tabloda 'lux' yok, 'Lux' var. İkincisi hata verir: column "adi" does not exist, altında da HINT: Perhaps you meant to reference the column "hesaplar.ad". Yanlış bir değer sessizce boş sonuç verir, yanlış bir sütun adı hata verir.

Soru 3

stok sütununda NOT NULL DEFAULT 0 var. Bu komut ne yapar?

SQL
INSERT INTO dukkan (ad, fiyat, stok)VALUES ('Kalkan', 400, NULL);
Cevabı göster

Hata verir: null value in column "stok" of relation "dukkan" violates not-null constraint. DEFAULT yalnız sütun hiç yazılmadığında ya da yerine DEFAULT yazıldığında devreye girer. Açıkça NULL yazdın; Postgres onu koymaya çalıştı ve NOT NULL durdurdu. stok'u listeden çıkarsaydın 0 alırdı.

Soru 4

altin sütununda yalnız CHECK (altin >= 0) var, NOT NULL yok. Altını NULL olan bir satır eklenebilir mi?

Cevabı göster

Eklenebilir. CHECK yalnız sonucu yanlış çıkan satırı reddeder; NULL >= 0 "bilinmiyor" verir ve geçer. Sütun boş kalmamalıysa CHECK'in yanına NOT NULL da yazmalısın.

Soru 5

Mesajı oku: hangi tablo, hangi kural, hangi satır?

Hata çıktısı
ERROR:  new row for relation "kadro" violates check constraint "kadro_hp_check"DETAIL:  Failing row contains (4, Soraka, destek, -50, 1).
Cevabı göster

Tablo kadro. Çiğnenen kural kadro_hp_check, yani hp sütunundaki CHECK. Reddedilen satır 4 numaralı Soraka: değişiklik onun canını -50 yapacaktı. Satır yazılmadı; transaction içindeysen transaction da bozuldu ve ROLLBACK bekliyor.

Soru 6

Bu dükkân kaydı iki kuralı birden çiğniyor. Hangi mesajı görürsün? Yalnız fiyatı düzeltip yeniden çalıştırırsan mesaj değişir mi?

SQL
INSERT INTO dukkan (ad, fiyat)VALUES (NULL, -5);
Cevabı göster

null value in column "ad" of relation "dukkan" violates not-null constraint. Ad boş (NOT NULL) ve fiyat eksi (CHECK), ama kapılar sırayla denetlenir ve NOT NULL kapısı CHECK'ten önce gelir. Yalnız fiyatı düzeltirsen mesaj değişmez, çünkü satır hâlâ aynı kapıda, adda takılıyor. Tersini yapıp adı düzeltirsen ve fiyatı -5 bırakırsan bu kez new row for relation "dukkan" violates check constraint "dukkan_fiyat_check" gelir.

Soru 7

Transaction içinde ikinci UPDATE hata verdi. Sen yine de COMMIT; yazıp çalıştırdın. İlk UPDATE'in değişikliği kaydedilir mi?

Cevabı göster

Hayır. Bozulmuş bir transaction kaydedilemez: Postgres o COMMIT'i ROLLBACK gibi uygular ve transaction'daki bütün değişiklikleri atar. Ya hep ya hiç kuralını COMMIT bile bozamaz. Doğrusu yine de açıkça ROLLBACK yazmaktır; ne olduğunu kodu okuyan herkes görür.

Soru 8

Neon'un Free planındasın. Dün akşam yanlışlıkla bir tabloyu sildin, bugün öğlen fark ettin. Geçmişten geri yükleme seni kurtarır mı?

Cevabı göster

Kurtarmaz. Free planda geçmiş penceresi 6 saat; dün akşamki an çoktan dışarıda kaldı. Seni ancak o andan önce aldığın bir snapshot ya da pg_dump dosyası kurtarır. Bu yüzden yedek, ihtiyaç duymadan önce alınır.

Kayıt noktası

Bu dersteki SQL'in Neon'un düzenleyicisinde kaldı; History onu bir süre tutar ama kalıcı bir yer değil. Ders 34'teki ders34.sql gibi, bu dersin kalıplarını da bir dosyada tut: bir sonraki tabloyu tasarlarken ya da bir satırı silmeden önce dönüp bakarsın. VS Code'da C:\kod\veritabani klasörünü aç ve ders35.sql adlı bir dosyaya şunları yaz:

ders35.sql — veritabani — Visual Studio Code
ders35.sql
-- Ders 35: veriyi değiştirmek ve tablo tasarımı-- Kalıp defteri: gerekeni seçip SQL Editor'e kopyala. -- Kurallı tablo: bozuk satır içeri giremezCREATE TABLE dukkan (    id integer GENERATED ALWAYS AS IDENTITY PRIMARY KEY,    ad varchar(30) NOT NULL UNIQUE CHECK (ad <> ''),    fiyat integer NOT NULL CHECK (fiyat > 0),    stok integer NOT NULL DEFAULT 0 CHECK (stok >= 0)); -- Silmeden önce aynı WHERE ile bak, sonra silSELECT id, ad FROM hesaplar WHERE altin IS NULL;DELETE FROM hesaplar WHERE altin IS NULL RETURNING id, ad; -- Dolu tabloya sonradan kural eklemekALTER TABLE hesaplar ALTER COLUMN ad SET NOT NULL;ALTER TABLE hesaplar ADD UNIQUE (ad);ALTER TABLE hesaplar ADD CHECK (altin >= 0); -- Altın transferi: ya ikisi birden ya hiçbiriBEGIN;UPDATE hesaplar SET altin = altin + 200 WHERE ad = 'Teemo';UPDATE hesaplar SET altin = altin - 200 WHERE ad = 'Jinx';COMMIT;
⎇ masterLn 1, Col 1Spaces: 4UTF-8SQL

Bu dosya bir kurulum betiği değil, bir kalıp defteri: içindeki komutlar bu derste zaten çalıştı. Baştan sona yeniden çalıştırırsan ilk komut relation "dukkan" already exists hatası verir; transfer bloğunu tek başına çalıştırırsan da Jinx'ten Teemo'ya ikinci kez 200 altın gider. Gerekeni seçip kopyala.

Git'i bu klasörde Ders 33'te başlattın; yalnız yeni bir kayıt alacaksın. VS Code terminalinde, bu klasördeyken:

Bir sonraki derste tabloları birbirine bağlayacaksın: bir oyuncunun maçlarını ayrı bir tabloda tutup yabancı anahtarla oyuncuya bağlayacak, JOIN ile ikisini tek sorguda birleştireceksin.

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