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
INSERTile tek ve çok satır ekleyipRETURNINGile veritabanının verdiğiid'yi aynı komutta geri alabilirsinUPDATE … SET … WHEREile yalnız istediğin satırları değiştirebilir,DELETE'ten önce aynıWHERE'iSELECTile deneyebilirsinNOT NULL,UNIQUE,CHECKveDEFAULTile bozuk verinin tabloya girmesini engelleyebilir, ihlal mesajından hangi kuralın çiğnendiğini bulabilirsinALTER TABLEile dolu bir tabloya sütun ve kural ekleyebilir, sütunun adını değiştirebilir ya da onu silebilirsinBEGIN,COMMITveROLLBACKile birbirine bağlı değişiklikleri ya hep birlikte ya hiç yapabilirsin- Riskli bir işten önce Neon'da kayıt noktası alabilir,
pg_dumpile veritabanını bir dosyaya yedekleyebilirsin
Bu sayfada
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:
-- 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.
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:
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;| id | oyuncu | sampiyon |
|---|---|---|
| 21 | Deniz | Lux |
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:
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;| id | ad |
|---|---|
| 1 | Ahri |
| 2 | Lux |
| 3 | Garen |
| 4 | Jinx |
| 5 | Teemo |
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
INSERT INTO hesaplar (ad)VALUES ('Yasuo')RETURNING id, ad, altin, seviye;| id | ad | altin | seviye |
|---|---|---|---|
| 6 | Yasuo | NULL | NULL |
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:
INSERT INTO hesaplar (ad, altin, seviye)VALUES ('Annie', 200);ERROR: INSERT has more target columns than expressionstarget 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:
INSERT INTO hesaplar (ad, altin, seviye)VALUES ('Annie', 200, 1), ('Zed', 'yüz', 2);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.
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.
syntax error at or near "("Ne zaman çıkar? Çok satırlı INSERT'te iki satırın arasına virgül koymayı unutunca.
INSERT INTO hesaplar (ad, altin, seviye)VALUES ('Vex', 100, 1) ('Zed', 200, 2);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.
UPDATE hesaplarSET altin = 650WHERE ad = 'Ahri'RETURNING id, ad, altin;| id | ad | altin |
|---|---|---|
| 1 | Ahri | 650 |
| Parça | Anlamı |
|---|---|
UPDATE hesaplar | hangi tablo |
SET altin = 650 | hangi sütuna hangi yeni değer |
WHERE ad = 'Ahri' | hangi satırlar; Ders 34'teki WHERE'in aynısı |
RETURNING id, ad, altin | değ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:
UPDATE hesaplarSET altin = altin + 100, seviye = seviye + 1WHERE ad = 'Garen'RETURNING ad, altin, seviye;| ad | altin | seviye |
|---|---|---|
| Garen | 220 | 3 |
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:
UPDATE hesaplarSET altin = altin + 50WHERE seviye = 1RETURNING ad, altin;| ad | altin |
|---|---|
| Teemo | 100 |
| Ahri | 700 |
İ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
UPDATE hesaplarSET altin = 0WHERE ad = 'ahri'RETURNING ad, altin;| ad | altin |
|---|
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:
UPDATE hesaplarSET altn = 500WHERE ad = 'Lux';ERROR: column "altn" of relation "hesaplar" does not existYanlış 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ı:
UPDATE maclarSET olum = 1WHERE id = 16RETURNING id, oyuncu, sampiyon, kill, olum, asist;| id | oyuncu | sampiyon | kill | olum | asist |
|---|---|---|---|---|---|
| 16 | Ece | Ashe | 7 | 1 | 12 |
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ı:
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:
SELECT id, ad, altin, seviyeFROM hesaplarWHERE altin IS NULL;| id | ad | altin | seviye |
|---|---|---|---|
| 6 | Yasuo | NULL | NULL |
Tek satır, beklediğin satır. Şimdi baştaki SELECT satırını DELETE yap:
DELETE FROM hesaplarWHERE altin IS NULLRETURNING id, ad;| id | ad |
|---|---|
| 6 | Yasuo |
- Değiştirmek ya da silmek istediğin satırları
SELECT … WHERE …ile seç. - Gelen satırlara ve sayısına bak. Fazla ya da eksikse
WHERE'i düzelt, yeniden seç. SELECT …kısmınıDELETE(ya daUPDATE … SET …) ile değiştir;WHERE'i aynen bırak.RETURNINGile 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:
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ı:
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.
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:
BEGIN;UPDATE hesaplar SET altin = 0;SELECT ad, altin FROM hesaplar ORDER BY id;ROLLBACK;| ad | altin |
|---|---|
| Ahri | 0 |
| Lux | 0 |
| Garen | 0 |
| Jinx | 0 |
| Teemo | 0 |
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ı:
SELECT ad, altin FROM hesaplar ORDER BY id;| ad | altin |
|---|---|
| Ahri | 700 |
| Lux | 450 |
| Garen | 220 |
| Jinx | 800 |
| Teemo | 100 |
Kısıtlar: bozuk veriyi kapıda durdurmak
Kuralsız tablonun nelere izin verdiğine bak:
INSERT INTO hesaplar (ad, altin, seviye)VALUES (NULL, -500, 0), ('Lux', 999999, 1)RETURNING id, ad, altin, seviye;| id | ad | altin | seviye |
|---|---|---|---|
| 7 | NULL | -500 | 0 |
| 8 | Lux | 999999 | 1 |
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:
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ım | Ne 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 NULL | Bu sütun boş (NULL) kalamaz. |
UNIQUE | Bu 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 0 | Sü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:
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;| id | ad | stok |
|---|---|---|
| 1 | Uzun Kılıç | 10 |
| 2 | Can İksiri | 99 |
| 3 | Sonsuzluk 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
null value in column "ad" of relation "dukkan" violates not-null constraintNe 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).
INSERT INTO dukkan (ad, fiyat)VALUES (NULL, 300);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
duplicate 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.
INSERT INTO dukkan (ad, fiyat)VALUES ('Can İksiri', 40);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.
new 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.
INSERT INTO dukkan (ad, fiyat)VALUES ('Bedava Kılıç', 0);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:
UPDATE dukkanSET stok = stok - 3WHERE ad = 'Sonsuzluk Kılıcı';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:
INSERT INTO dukkan (ad, fiyat)VALUES ('', 100);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:
INSERT INTO dukkan (ad, fiyat, stok)VALUES ('Çizme', 300, 5), ('Kontrol Totemi', 75, DEFAULT)RETURNING id, ad, stok;| id | ad | stok |
|---|---|---|
| 8 | Çizme | 5 |
| 9 | Kontrol Totemi | 0 |
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)
value 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.
INSERT INTO dukkan (ad, fiyat)VALUES ('Kadim Ejderhanın Alevli Kılıcı ve Kalkanı', 4000);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:
Ö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:
ALTER TABLE hesaplar ADD CHECK (altin >= 0);ERROR: check constraint "hesaplar_altin_check" of relation "hesaplar" is violated by some rowPostgres 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:
SELECT id, ad, altin, seviyeFROM hesaplarWHERE ad IS NULL OR altin < 0 OR seviye < 1;| id | ad | altin | seviye |
|---|---|---|---|
| 7 | NULL | -500 | 0 |
Aynı adla iki hesap var mı? GROUP BY ve HAVING ile say:
SELECT ad, COUNT(*) AS kac_taneFROM hesaplarGROUP BY adHAVING COUNT(*) > 1;| ad | kac_tane |
|---|---|
| Lux | 2 |
SELECT id, ad, altin, seviyeFROM hesaplarWHERE ad = 'Lux'ORDER BY id;| id | ad | altin | seviye |
|---|---|---|---|
| 2 | Lux | 450 | 3 |
| 8 | Lux | 999999 | 1 |
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:
DELETE FROM hesaplarWHERE id IN (7, 8)RETURNING id, ad;| id | ad |
|---|---|
| 7 | NULL |
| 8 | Lux |
Şimdi kurallar:
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:
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ç.
INSERT INTO hesaplar (ad)VALUES ('Annie')RETURNING id, ad, altin, seviye;| id | ad | altin | seviye |
|---|---|---|---|
| 9 | Annie | 500 | 1 |
Yasuo'nun başına gelen bir daha olmaz: NULL yerine varsayılanlar geldi.
Sütun eklemek
ALTER TABLE hesaplar ADD COLUMN unvan text;SELECT id, ad, unvan FROM hesaplar ORDER BY id;| id | ad | unvan |
|---|---|---|
| 1 | Ahri | NULL |
| 2 | Lux | NULL |
| 3 | Garen | NULL |
| 4 | Jinx | NULL |
| 5 | Teemo | NULL |
| 9 | Annie | NULL |
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:
ALTER TABLE hesaplar ADD COLUMN aktif boolean NOT NULL DEFAULT true;SELECT ad, aktif FROM hesaplar ORDER BY id;| ad | aktif |
|---|---|
| Ahri | true |
| Lux | true |
| Garen | true |
| Jinx | true |
| Teemo | true |
| Annie | true |
column "eposta" of relation "hesaplar" contains null valuesNe 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.
ALTER TABLE hesaplar ADD COLUMN eposta text NOT NULL;ERROR: column "eposta" of relation "hesaplar" contains null valuesYeni 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
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:
SELECT ad, unvan FROM hesaplar;ERROR: column "unvan" does not existDers 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:
ALTER TABLE hesaplar DROP COLUMN lakap;SELECT * FROM hesaplar ORDER BY id;| id | ad | altin | seviye | aktif |
|---|---|---|---|---|
| 1 | Ahri | 700 | 1 | true |
| 2 | Lux | 450 | 3 | true |
| 3 | Garen | 220 | 3 | true |
| 4 | Jinx | 800 | 5 | true |
| 5 | Teemo | 100 | 1 | true |
| 9 | Annie | 500 | 1 | true |
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 eklemek | ALTER TABLE t ADD COLUMN sutun tip; |
| Sütun silmek | ALTER TABLE t DROP COLUMN sutun; |
| Sütunun adını değiştirmek | ALTER TABLE t RENAME COLUMN eski TO yeni; |
| Tablonun adını değiştirmek | ALTER TABLE t RENAME TO yeni_ad; |
| Boş geçilemez yapmak | ALTER TABLE t ALTER COLUMN sutun SET NOT NULL; |
| Varsayılan vermek | ALTER TABLE t ALTER COLUMN sutun SET DEFAULT deger; |
| Kural eklemek | ALTER 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:
UPDATE hesaplarSET altin = altin + 500WHERE ad = 'Teemo'RETURNING ad, altin;| ad | altin |
|---|---|
| Teemo | 600 |
UPDATE hesaplarSET altin = altin - 500WHERE ad = 'Lux';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ç?
SELECT ad, altinFROM hesaplarWHERE ad IN ('Lux', 'Teemo')ORDER BY id;| ad | altin |
|---|---|
| Lux | 450 |
| Teemo | 600 |
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:
UPDATE hesaplarSET altin = altin - 500WHERE ad = 'Teemo'RETURNING ad, altin;| ad | altin |
|---|---|
| Teemo | 100 |
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:
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;| ad | altin |
|---|---|
| Jinx | 600 |
| Teemo | 300 |
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:
BEGIN;UPDATE hesaplar SET altin = altin + 500 WHERE ad = 'Teemo';UPDATE hesaplar SET altin = altin - 500 WHERE ad = 'Lux';COMMIT;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.
current transaction is aborted, commands ignored until end of transaction blockNe zaman çıkar? Transaction içindeki bir komut hata verdikten sonra, ROLLBACK demeden aynı bağlantıdan yeni bir komut gönderince.
SELECT ad, altin FROM hesaplar;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:
ROLLBACK;SELECT ad, altin FROM hesaplar WHERE ad IN ('Lux', 'Teemo') ORDER BY id;| ad | altin |
|---|---|
| Lux | 450 |
| Teemo | 300 |
Teemo'nun +500'ü de geri alındı. Ya ikisi birden ya hiçbiri: bu kez altın yoktan var olmadı.
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,SELECTile bak, sonraCOMMITya daROLLBACK. - 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.
| Yol | Nerede durur? | Ne zaman? | Neon Free planda |
|---|---|---|---|
| Geçmişten geri yükleme | Neon | "Az önce bir şeyi bozdum." | son 6 saat |
| Snapshot | Neon | Büyük bir değişiklikten hemen önce | aynı anda 1 tane |
| Dal (branch) | Neon | Riskli bir komutu önce kopyada denemek | projede 10 dal |
pg_dump dosyası | Senin bilgisayarın | Düzenli, Neon'dan bağımsız kopya | sı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:
- Neon'da projeni aç, sol menüde Postgres database altındaki Backup & Restore sayfasına gir.
- Restore from history'ye tıkla. From history sekmesinde dönmek istediğin tarihi ve saati seç, Next'e bas.
- Ö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:
- 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_dumpkendi sürümünden eski bir Postgres'i yedekleyebilir, yenisini yedekleyemez. Projendeki sürümü görmek için SQL Editor'deSHOW server_version;çalıştır. En kolayı en yeni sürümü seçmek (Ekim 2026'da 18). - 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.
- Kurulum bitince araçlar
C:\Program Files\PostgreSQL\18\binklasö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:
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 : CommandNotFoundExceptionDers 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:
& "C:\Program Files\PostgreSQL\18\bin\pg_dump.exe" --versionBu 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:
yedek.sqlBağ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):
& "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çenek | Ne yapar? |
|---|---|
--dbname="…" | Hangi veritabanı: bağlantı dizesi. |
--no-owner | Tabloların sahibini (Neon'daki kullanıcı adını) dosyaya yazmaz; dosya başka bir veritabanına sorunsuz yüklenir. |
--inserts | Satı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:
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)));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 TABLEbu derste koyduğun kuralları taşıyor:NOT NULL,DEFAULT,CHECK.UNIQUEve birincil anahtar dosyanın sonlarına doğru ayrıALTER TABLEsatı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ığınHINT'i hatırla: bu yol, kayıtları bir veritabanından ötekine taşırken kullanılır. Yedek tam olarak bunu yapıyor.- Satırlar
idsırasıyla değil:pg_dumpsatı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
SETsatırları ve\restrictile 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.
& "C:\Program Files\PostgreSQL\18\bin\psql.exe" --dbname="BOŞ_VERİTABANININ_DİZESİ" --file=yedek.sqlSık hatalar
Bu derste karşına çıkabilecek öteki hatalar. Bütün derslerin hataları hata rehberinde toplanıyor.
check constraint "hesaplar_altin_check" of relation "hesaplar" is violated by some rowNe 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.
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.
The '--' operator works only on variables or on properties.Ne zaman çıkar? Tırnaklı bir program yolunu başına & koymadan çalıştırınca.
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 : UnexpectedTokenPowerShell 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ç.
Kadroyu kur
Önce tabloyu oluştur:
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ç:
| id | ad | hp |
|---|---|---|
| 1 | Garen | 690 |
| 2 | Ahri | 590 |
| 3 | Jinx | 630 |
| 4 | Soraka | 600 |
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
INSERT INTO kadro (ad, rol, hp)VALUES ('Garen', 'üst', 690), ('Ahri', 'orta', 590), ('Jinx', 'alt', 630), ('Soraka', 'destek', DEFAULT)RETURNING id, ad, hp;| id | ad | hp |
|---|---|---|
| 1 | Garen | 690 |
| 2 | Ahri | 590 |
| 3 | Jinx | 630 |
| 4 | Soraka | 600 |
Aynı komutu bir kez daha çalıştırınca UNIQUE kuralı ilk satırda durdurur ve dört satırdan hiçbiri eklenmez:
INSERT INTO kadro (ad, rol, hp)VALUES ('Garen', 'üst', 690), ('Ahri', 'orta', 590), ('Jinx', 'alt', 630), ('Soraka', 'destek', DEFAULT)RETURNING id, ad, hp;ERROR: duplicate key value violates unique constraint "kadro_ad_key"DETAIL: Key (ad)=(Garen) already exists.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):
| ad | hp |
|---|---|
| Jinx | 480 |
| Soraka | 450 |
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
UPDATE kadroSET hp = hp - 150WHERE rol IN ('alt', 'destek')RETURNING ad, hp;| ad | hp |
|---|---|
| Jinx | 480 |
| Soraka | 450 |
481 hasar reddedilir; Jinx'in canı değişmez:
UPDATE kadroSET hp = hp - 481WHERE ad = 'Jinx';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:
UPDATE kadroSET hp = hp - 480WHERE ad = 'Jinx'RETURNING ad, hp;| ad | hp |
|---|---|
| Jinx | 0 |
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ç:
| id | ad |
|---|---|
| 3 | Jinx |
İ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
SELECT id, ad, hpFROM kadroWHERE hp = 0;| id | ad | hp |
|---|---|---|
| 3 | Jinx | 0 |
Sınırı ıskalamak böyle görünür; hata yok ama kimse gelmiyor:
SELECT id, ad, hpFROM kadroWHERE hp < 0;| id | ad | hp |
|---|
Doğru WHERE ile sil:
DELETE FROM kadroWHERE hp = 0RETURNING id, ad;| id | ad |
|---|---|
| 3 | Jinx |
Kadroya yama
kadro tablosunu ALTER TABLE ile değiştir:
seviyeadında bir sütun ekle: tam sayı, boş geçilemez, varsayılanı 1, değeri 1 ile 18 arasında olmalı.rolsütununun adınıkoridoryap.koridoryalnı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:
| ad | koridor | hp | seviye |
|---|---|---|---|
| Garen | üst | 690 | 1 |
| Ahri | orta | 590 | 1 |
| Soraka | destek | 450 | 1 |
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
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;| ad | koridor | hp | seviye |
|---|---|---|---|
| Garen | üst | 690 | 1 |
| Ahri | orta | 590 | 1 |
| Soraka | destek | 450 | 1 |
Lux'un canı ve seviyesi varsayılanlardan gelir:
INSERT INTO kadro (ad, koridor)VALUES ('Lux', 'orta')RETURNING ad, koridor, hp, seviye;| ad | koridor | hp | seviye |
|---|---|---|---|
| Lux | orta | 600 | 1 |
'ust' listede yok (ü eksik):
INSERT INTO kadro (ad, koridor)VALUES ('Yasuo', 'ust');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:
UPDATE kadroSET seviye = 18WHERE ad = 'Ahri'RETURNING ad, seviye;| ad | seviye |
|---|---|
| Ahri | 18 |
UPDATE kadroSET seviye = 19WHERE ad = 'Ahri';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ı.
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:
DELETE FROM kadro;SELECT COUNT(*) AS kalan FROM kadro;kalan 0 çıkar. Dalı production yapıp yalnız sayımı çalıştırınca:
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.
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:
| ad | hp |
|---|---|
| Garen | 890 |
| Soraka | 250 |
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
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;| ad | hp |
|---|---|
| Garen | 890 |
| Soraka | 250 |
300'lük aktarım:
BEGIN;UPDATE kadro SET hp = hp + 300 WHERE ad = 'Garen';UPDATE kadro SET hp = hp - 300 WHERE ad = 'Soraka';COMMIT;ERROR: new row for relation "kadro" violates check constraint "kadro_hp_check"DETAIL: Failing row contains (4, Soraka, destek, -50, 1).ROLLBACK;SELECT ad, hp FROM kadro WHERE ad IN ('Garen', 'Soraka') ORDER BY id;| ad | hp |
|---|---|
| Garen | 890 |
| Soraka | 250 |
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?
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?
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?
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?
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?
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:
-- 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;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.