Veritabanı Yönetim Sistemleri
Veri Yazmak: INSERT, DELETE, UPDATE
Dr. Öğr. Üyesi Oğuz Emre Kural
2026
Öğrenme Kazanımları
Bu dersin sonunda:
INSERT INTO ile tek ve çok satırlı ekleme yapabilir, kısıt hatalarını okuyabilirsiniz.
Bir CSV dosyasını .import ile tabloya aktarabilir, kimlik sütunu olmayan veriyi geçici tablo üzerinden ekleyebilirsiniz.
DELETE ile satır silebilir, yabancı anahtarın silmeye etkisini ve ON DELETE seçeneklerini açıklayabilirsiniz.
UPDATE ile değer düzeltebilir, trim, upper ve LIKE ile dağınık veriyi temizleyebilirsiniz.
Bir tetikleyici yazabilir ve kalıcı silme ile yumuşak silme arasında seçim yapabilirsiniz.
Üçüncü haftada SAMULAŞ şemasını kurduk ama tablolar boştu. Bugün aynı şemayı doldurup değiştireceğiz. Dersin ana hattı INSERT, DELETE, UPDATE; tetikleyici ve yumuşak silme son bölümde, zaman daralırsa haftaya kalabilir.
Geçen Dersten Bu Derse
Üçüncü haftada üç tabloyu tasarladık: cards, stations, swipes. Şema hazır, tablolar boş .
cards id
←
swipes id, card_id, station_id, type, datetime, amount_kurus
→
stations id, name, district
Bugün bu tablolara veri yazacağız: durak ekleyeceğiz, kart işlemleri kaydedeceğiz, hatalı kayıtları düzeltip sileceğiz.
swipes.card_id karta, swipes.station_id durağa bağlanır; iki ok bunu gösteriyor. Şemayı ezbere tekrar ettirmeyin; “Hangi tabloya önce veri girmeliyiz?” diye sorun. Yabancı anahtar yüzünden önce cards ve stations, sonra swipes dolmalı; bu sıra derste kendiliğinden ortaya çıkacak.
Veriyle Yapılan Dört İş
Okuma SELECT İlk iki haftada yaptık: sorgu, filtre, JOIN.
Ekleme INSERT Tabloya yeni satır yazar.
Silme DELETE Var olan satırı kaldırır.
Güncelleme UPDATE Var olan satırın değerini değiştirir.
Okumak için önce veri gerekir. Bu yüzden bugün eklemeyle başlıyoruz.
Bu dörtlü yazılım dünyasında CRUD diye anılır: create, read, update, delete. Kısaltmayı söyleyin ama ezberletmeyin. Dört işin de aynı tablolar üzerinde çalıştığını, yalnızca okumanın veriyi değiştirmediğini vurgulayın.
Bugünün Dosyaları
Boş bir çalışma klasörü açın ve şu dosyaları içine indirin:
Ardından terminalde o klasöre geçin ve yeni bir veritabanı oluşturun:
sqlite3 ulasim.db dosya yoksa oluşturur; bu kez bunu istiyoruz, -readonly kullanmıyoruz. Veri yazacağımız için ilk kez yazılabilir bir dosya açıyoruz. Dosya adlarını slayttaki gibi tutmalarını isteyin; komutlar bu adlara göre yazıldı.
Şemayı Yüklemek
Her oturumda bir satır daha gerekir:
PRAGMA foreign_keys = ON;
SQLite yabancı anahtarları ancak bu ayar açıkken denetler. Ayar bağlantıya özeldir; .quit deyip geri gelince yeniden yazılır.
schema.sql dosyasının ilk satırı bu PRAGMA’yı içeriyor, ama yalnızca .read yapılan bağlantıda geçerli. Dersin silme bölümünde yabancı anahtar hataları göreceğiz; o sırada birinin hata almaması büyük olasılıkla bu satırı atladığı içindir. Çıktı ayarlarını da hatırlatın: .headers on, .mode box, .nullvalue NULL.
INSERT: Tabloya Yeni Satır Eklemek
Şimdiye kadar tablolardan okuduk. Şimdi tabloya bir şey yazacağız : yeni bir durak.
name Yurtlar district Atakum
stations
1
OMÜ Rektörlük
Atakum
2
Yurtlar
Atakum
Bir ekleme üç şeyi bilmek ister: hangi tablo, hangi sütunlar, hangi değerler.
Önce kartı gösterin: elimizde bir durağın adı ve ilçesi var, tabloda yeri yok. İlk ilerlemede satır tabloya girer. İkincisinde kimlik belirir; SQLite’ın verdiğini söyleyin, ayrıntısı birkaç slayt sonra. Sözdizimi henüz yok; önce “satır eklemek” fikri otursun.
INSERT Sözdizimi
INSERT INTO "stations" ("id" , "name" , "district" )
VALUES (1 , 'OMÜ Rektörlük' , 'Atakum' );
INSERT INTO tablo Hangi tabloya?
(sütunlar) Hangi sütunlara?
VALUES (değerler) Hangi değerler?
Sütunlar ve değerler sırayla eşleşir : birinci sütun birinci değeri, ikinci sütun ikinci değeri alır.
Altı adım: tablo, sütun listesi, değer listesi, sonra üç eşleşme. Eşleşmeleri renkle gösterirken “üçüncü değer hangi sütuna gider?” diye sorun. Metin değerlerinin tek tırnakla, sütun adlarının çift tırnakla yazıldığını ilk haftadan hatırlatın. Sütun sırası tablodaki sırayla aynı olmak zorunda değil; listede ne yazdıysa o sıra geçerli.
İlk Durağı Ekleyelim
INSERT INTO "stations" ("id" , "name" , "district" )
VALUES (1 , 'OMÜ Rektörlük' , 'Atakum' );
Ekran boş kalır; SQLite başarılı yazmada mesaj vermez. Kontrol için okuyalım:
SELECT * FROM "stations" ;
“Nothing happened” hissi öğrenciyi şaşırtır; SELECT ile doğrulamayı alışkanlık yapın. Bu derste her yazma işleminden sonra SELECT çalıştıracağız. İkinci durağı da aynı yolla ekletin: (2, 'Yurtlar', 'Atakum').
Kimliği Kim Versin?
İkinci durağı id = 2 ile ekledik. Üçüncüde kimliği hiç yazmayalım:
INSERT INTO "stations" ("name" , "district" )
VALUES ('Türkiş' , 'Atakum' );
1
OMÜ Rektörlük
Atakum
2
Yurtlar
Atakum
3
Türkiş
Atakum
id sütunu INTEGER PRIMARY KEY olduğu için SQLite en büyük kimliğe bir ekler. Kimliği elle vermek, var olanı tekrar yazma riskidir.
Durak adını bilerek “Türkiş” diye yazdık; üçüncü haftada bu yazımın “Türk-İş” olması gerektiğini konuşmuştuk. UPDATE bölümünde düzelteceğiz, şimdi söylemeyin. Kimliği kendimiz yazarsak ikinci bir 2 girmeye çalışıp hata alırız; bu yüzden kimliği SQLite’a bırakmak varsayılan alışkanlık olmalı. Silinen kimliklerin yeniden kullanımı DELETE bölümünde.
Kısıtlar Kapıda Bekler
Üçüncü haftada name için NOT NULL UNIQUE yazmıştık. Aynı durağı bir kez daha ekleyelim:
name Türkiş district Atakum
UNIQUE constraint failed: stations.name
stations
1
OMÜ Rektörlük
Atakum
2
Yurtlar
Atakum
3
Türkiş
Atakum
INSERT INTO "stations" ("name" , "district" ) VALUES ('Türkiş' , 'Atakum' );
Runtime error: UNIQUE constraint failed: stations.name (19)
Kısıtlar bizi ve diğer kullanıcıları yanlış veriden korur. Hata mesajını okutun: hangi kural, hangi tablo, hangi sütun. Aynı sorguyu bir de NULL adla deneyin: NOT NULL constraint failed: stations.name. İki durumda da tablo değişmez; SELECT ile gösterin.
NOT NULL ve CHECK de Aynı Kapıda
INSERT INTO "stations" ("name" , "district" ) VALUES (NULL , 'Atakum' );
Runtime error: NOT NULL constraint failed: stations.name (19)
swipes tablosundaki tutar kuralı da eklemeyi durdurur. Biniş tutarı eksi olmalıydı:
INSERT INTO "swipes" ("card_id" , "station_id" , "type" , "datetime" , "amount_kurus" )
VALUES (17 , 1 , 'boarding' , '2026-09-02 08:00:00' , 3900 );
Runtime error: CHECK constraint failed: ...
Hata alan INSERT hiçbir şey yazmaz; tablo önceki hâlinde kalır.
CHECK hatasında SQLite kuralın metnini basar; uzun olduğu için slaytta kısalttık. Üçüncü haftadaki kural tabloya dönüp “boarding için tutar neden eksi olmalıydı?” diye sorun. Bu sorgu için cards tablosunda 17 numaralı kart olmalı; yoksa CHECK yerine FOREIGN KEY hatası görürler, bu da iyi bir konuşma başlatır.
Uygulama 1: Kartları Ekleyin
Bir dakika: cards tablosunun tek sütunu var: id. 17 ve 18 numaralı iki kartı ekleyin, sonra tabloyu okuyun.
INSERT INTO "cards" ("id" ) VALUES (17 );
INSERT INTO "cards" ("id" ) VALUES (18 );
SELECT * FROM "cards" ;
Kartlar ileride swipes satırlarının bağlanacağı kayıtlar; önce onlar var olmalı.
İki ayrı INSERT yazmaları normal; bir sonraki slaytta ikisini tek ifadede yazmanın yolunu göstereceğiz. Burada kimliği elle veriyoruz çünkü kart numarası dış dünyada basılı bir numara; sıradaki sayı değil. Bu, “kimliği ne zaman biz veririz?” sorusunun cevabı.
Birçok Satırı Tek Seferde
Üç durak birden geldi. Üç ayrı INSERT yerine değer listelerini virgülle ayırırız:
name Mimarsinan district Atakum
name Atakent district Atakum
name Cumhuriyet Meydanı district İlkadım
stations
3
Türkiş
Atakum
4
Mimarsinan
Atakum
5
Atakent
Atakum
6
Cumhuriyet Meydanı
İlkadım
INSERT INTO "stations" ("name" , "district" ) VALUES
('Mimarsinan' , 'Atakum' ),
('Atakent' , 'Atakum' ),
('Cumhuriyet Meydanı' , 'İlkadım' );
Her satır kendi parantezinde, aralarında virgül, en sonda noktalı virgül. Bu yalnızca yazım kolaylığı değil: yüz satırı tek ifadede eklemek yüz ayrı ifadeden daha hızlıdır. Tablonun ilk iki satırı yer kazanmak için gösterilmedi; SELECT ile tamamını açın.
Ya Satırlardan Biri Hatalıysa?
INSERT INTO "stations" ("name" , "district" ) VALUES
('Deneme A' , 'Atakum' ),
('Yurtlar' , 'Atakum' );
Runtime error: UNIQUE constraint failed: stations.name (19)
Deneme A da eklenmedi. Çok satırlı INSERT ya tamamen yazılır ya hiç yazılmaz.
SELECT COUNT(*) ile satır sayısının 6’da kaldığını gösterin. Bu davranışın adı işlem, yani transaction; ilerleyen haftalarda ayrıntısına gireceğiz. Şimdilik “SQLite yarım iş bırakmaz” cümlesi yeter.
Kart İşlemlerini Kaydedelim
swipes tablosuna 17 ve 18 numaralı kartların hareketlerini yazalım. Yükleme işleminde durak yoktur; station_id için NULL yazarız:
INSERT INTO "swipes" ("card_id" , "station_id" , "type" , "datetime" , "amount_kurus" ) VALUES
(17 , NULL , 'topup' , '2026-09-01 07:50:00' , 10000 ),
(17 , 1 , 'boarding' , '2026-09-01 08:15:00' , - 3900 ),
(17 , 3 , 'refund' , '2026-09-01 08:45:00' , 900 ),
(18 , 2 , 'boarding' , '2025-12-20 17:05:00' , - 3900 ),
(18 , 1 , 'boarding' , '2025-12-21 09:30:00' , - 3900 );
NULL, tırnaksız yazılır; “bu değer yok” demektir. datetime sütununu boş bırakırsak DEFAULT CURRENT_TIMESTAMP çalışır ve o anki zamanı yazar.
Üçüncü haftanın 901 ve 902 numaralı örnek satırları burada gerçek INSERT oldu; kimlikleri SQLite verecek, 1’den başlar. 2025 tarihli iki biniş DELETE bölümünde silinmek için eklendi. CURRENT_TIMESTAMP UTC yazar; Türkiye saati değil. Bunu canlı deneyin: datetime sütununu atlayıp bir yükleme ekleyin, çıkan saate bakın.
CSV: Veri Çoğu Zaman Hazır Gelir
Dört yeni durağın listesi elimize bir dosya olarak ulaştı. Dosyada ne var?
id, name, district
7, Denizevleri, Atakum
8, Gar, İlkadım
9, Büyük Cami, İlkadım
10, Tekkeköy, Tekkeköy
duraklar.csv
7
Denizevleri
Atakum
8
Gar
İlkadım
9
Büyük Cami
İlkadım
10
Tekkeköy
Tekkeköy
duraklar.csv dosyasını bir metin düzenleyicide de açın; SQLite dışında da okunabildiğini göstermek için. Dosyanın ilk satırı başlık satırıdır; sonraki slaytta bunun neden atlanması gerektiğini soracağız. Bir CSV’yi elle INSERT ile aktarmanın dört satırda bile sıkıcı olduğunu söyleyin.
.import: Dosyadan Tabloya
SQLite isteminin kendi komutu CSV’yi satır satır tabloya ekler:
.import --csv --skip 1 duraklar.csv stations
--csv
Değerler virgülle ayrılmış
--skip 1
İlk satırı atla: başlık satırı, veri değil
duraklar.csv
Okunacak dosya
stations
Hedef tablo
SELECT COUNT(*) FROM "stations"; artık 10 döndürür.
Kimliği Dosya mı Vermeli?
duraklar.csv kimlikleri hazır getirdi: 7, 8, 9, 10. .import bunları olduğu gibi yazdı; kimlikleri dosya belirledi, veritabanı değil. Bu derste kuralımız tersiydi: INSERT yazarken id vermedik, SQLite verdi.
Çakışma riski Dosyada 3 yazsaydı tabloda 3 zaten var: o satır UNIQUE constraint failed ile reddedilir, kalanlar girer. Elimizde eksik bir aktarım kalır.
Gerçek dosyalar Elimize ulaşan listelerde çoğu zaman kimlik yoktur; olsa da bizim tablomuzun değil, başka bir sistemin numarasıdır.
Olması gereken dosya kimliksiz olandır: yeni-duraklar.csv yalnızca ad ve ilçe taşır. Kimliği yine SQLite versin.
Geçiş slaydı: ilk aktarım çalıştı ama ders boyunca savunduğumuz ilkeyi çiğnedi. Öğrencilere sorun: “Durak listesini bize gönderen kişi bizim tablomuzdaki kimlikleri nereden bilsin?” Çakışma iddiası denenmiştir: .import çakışan satırı hata mesajıyla atlar, diğer satırları ekler; komut durmaz, bu yüzden hata gözden kaçabilir. Sonuç: kimlik sütunu olmayan dosya bir aksilik değil, beklenen durumdur. Sonraki slayt bu dosyayı nasıl aktaracağımızı gösterir.
Kimlik Sütunu Olmayan CSV
yeni-duraklar.csv olması gerektiği gibi: yalnızca ad ve ilçe. Ama stations üç sütun bekler, dosya iki sütun verir; doğrudan .import sütunları eşleyemez. Çözüm üç adım:
yeni-duraklar.csv name,district
Belediye Evleri,Tekkeköy
Yeni Mahalle,Atakum
→ .import
temp
Belediye Evleri
Tekkeköy
Yeni Mahalle
Atakum
→ INSERT … SELECT
stations
10
Tekkeköy
Tekkeköy
11
Belediye Evleri
Tekkeköy
12
Yeni Mahalle
Atakum
Üç adımı sözlü anlatın; komutlar bir sonraki slaytta. Kimliği SQLite’ın vermesi için dosyayı önce kendi tablosuna alıp oradan stations tablosuna taşıyoruz; id sütununu biz yazmadığımız için üçüncü sütun kendiliğinden dolar. Hedef tablo olmayan bir ada .import yapınca SQLite tabloyu kendisi kurar ve başlık satırını sütun adı yapar; bu yüzden burada --skip kullanılmaz. Yeni tablonun bütün sütunları TEXT türündedir.
Adım 1: Dosyayı Kendi Tablosuna Al
.import --csv yeni-duraklar.csv temp
İlk aktarım komutuyla iki fark var:
--skip 1 yok
Başlık satırı artık veri değil, sütun adı olacak
Hedef temp db’de yok
Olmayan tabloyu SQLite dosyaya bakarak kendisi kurar
sqlite> .schema temp
CREATE TABLE IF NOT EXISTS "temp"(
"name" TEXT, "district" TEXT);
Komutu girince ekranda hiçbir şey olmaz; .schema temp ile tablonun kurulduğunu gösterin. Sütun adları dosyanın ilk satırından geldi; tür bilgisi dosyada olmadığı için SQLite her sütunu TEXT yaptı. temp adını başka bir şey yapacaksanız SQLite’ın temp şema adıyla karışmaması için gecici gibi bir ad da seçebilirsiniz.
--skip 1 Yazsaydık?
Başlığı atlayınca SQLite ilk veri satırını sütun adı sanır:
sqlite> .import --csv --skip 1 yeni-duraklar.csv yanlis
sqlite> .schema yanlis
CREATE TABLE IF NOT EXISTS "yanlis"(
"Belediye Evleri" TEXT, "Tekkeköy" TEXT);
sqlite> SELECT * FROM "yanlis";
Yeni Mahalle|Atakum
Bir durak sütun adına dönüştü, tabloda tek satır kaldı. Kural: var olan tabloya aktarırken başlığı atla; yeni tablo kurdururken başlığı bırak.
Bu slayt ters örnek; öğrencilerin kendi dosyalarında denemesi gerekmez. Hata mesajı yoktur, komut sessizce yanlış tabloyu kurar; bu yüzden .schema ile kontrol alışkanlığı önemli. Ters örneği gösterdiyseniz DROP TABLE "yanlis"; ile temizleyin.
temp İçinde Ne Var?
┌─────────────────┬──────────┐
│ name │ district │
├─────────────────┼──────────┤
│ Belediye Evleri │ Tekkeköy │
│ Yeni Mahalle │ Atakum │
└─────────────────┴──────────┘
Satırlar geldi ama kimlik yok ve typeof("name") her satırda text. Kimliği stations tablosu verecek.
SELECT typeof("name"), typeof("district") FROM "temp"; ile türleri gösterin: hepsi text. Burada ilçe gibi metin sütunlarında sorun yok; sayısal bir sütun olsaydı hedef tablonun INTEGER tanımı aktarım sırasında dönüştürecekti.
Adım 2: temp’ten stations’a Taşı
INSERT INTO "stations" ("name" , "district" )
SELECT "name" , "district" FROM "temp" ;
VALUES yerine bir sorgunun sonucu eklenir. Sol taraftaki sütun listesiyle SELECT listesi sırayla eşleşir; id iki tarafta da yok, SQLite verir.
sqlite> SELECT * FROM "stations" WHERE "id" >= 10;
┌────┬─────────────────┬──────────┐
│ id │ name │ district │
├────┼─────────────────┼──────────┤
│ 10 │ Tekkeköy │ Tekkeköy │
│ 11 │ Belediye Evleri │ Tekkeköy │
│ 12 │ Yeni Mahalle │ Atakum │
└────┴─────────────────┴──────────┘
INSERT ... SELECT bu dersin en güçlü araçlarından: bir tablodan diğerine toplu kopyalama. Sütun sayısı ve sırası iki tarafta eşleşmeli; adların aynı olması gerekmez. Belediye Evleri 11, Yeni Mahalle 12 kimliğini aldı: en büyük kimlik 10’du, SQLite devam etti.
Adım 3: Geçici Tabloyu Kaldır
sqlite> .tables
cards stations swipes
İş bitti, iz kalmadı. Üç adımın özeti:
.import dosyayı kendi tablosuna alır (başlık sütun adı olur).
INSERT ... SELECT satırları asıl tabloya taşır, kimlikleri SQLite verir.
DROP TABLE geçici tabloyu kaldırır.
DROP TABLE geçen haftadan. Geçici tabloyu bırakmak zarar vermez ama şemayı kirletir; .tables çıktısında yalnızca üç tablonun kalmasını hedefleyin.
CSV’den Gelen Verinin İki Tuzağı
Her şey metin olarak gelir Yeni oluşturulan tabloda tüm sütunlar TEXT. Var olan tabloya aktarırken sütun türü dönüşümü yapar.
Boş hücre NULL değildir Dosyada virgülden sonra hiçbir şey yoksa SQLite oraya boş metin ’’ yazar. IS NULL bunu bulmaz.
SELECT "name" , typeof("district" ), "district" IS NULL , "district" = ''
FROM "temp" ;
Boş hücreleri gerçek NULL yapmak için az sonra göreceğimiz UPDATE gerekir.
Tablodaki örnek, ilçesi boş bırakılmış bir satırla hazırlanmış küçük bir deneme dosyasından; öğrencinin dosyalarında yok. Birinci tuzak için duraklar.csv aktarımında typeof("id") sonucunun integer olduğunu hatırlatın: var olan tablonun INTEGER sütunu metni dönüştürdü. İkinci tuzak NOT NULL kuralını da atlatır: '' boş metindir, NULL değil.
DELETE: Satırı Kaldırmak
Bir deneme durağı eklemiştik; artık gerekmiyor. Silme, bir satırı tablodan tamamen kaldırır.
stations
10
Tekkeköy
Tekkeköy
11
Belediye Evleri
Tekkeköy
12
Yeni Mahalle
Atakum
13
Deneme Durağı
Atakum
DELETE FROM "stations"
WHERE "name" = 'Deneme Durağı';
DELETE FROM tablo WHERE koşul; WHERE, ilk haftadan tanıdık: hangi satırlar?
Önce canlı olarak INSERT INTO "stations" ("name", "district") VALUES ('Deneme Durağı', 'Atakum'); ile deneme satırını ekletin, kimliği 13 olur. Sonra slayttaki DELETE. Silmenin geri alınamadığını söyleyin; yedek yoksa veri gitti. Yumuşak silme konusu dersin sonunda.
WHERE Olmazsa Ne Olur?
stations
1
OMÜ Rektörlük
Atakum
2
Yurtlar
Atakum
3
Türkiş
Atakum
4
Mimarsinan
Atakum
DELETE FROM "stations";
WHERE yok: 12 satırın hepsi silinir
DROP TABLE tabloyu yapısıyla siler; koşulsuz DELETE yapıyı bırakıp bütün satırları siler.
Bunu sınıfta çalıştırmayın. Alışkanlık önerisi: silmeden önce aynı WHERE ile bir SELECT çalıştırıp kaç satır seçildiğine bakmak. Bu alışkanlık UPDATE için de geçerli olacak.
Birden Çok Satırı Silmek
Kart hareketlerinin 2026 öncesi kayıtları arşive gitti; tablodan kaldıralım. Önce seçelim:
SELECT "id" , "card_id" , "datetime" FROM "swipes"
WHERE "datetime" < '2026-01-01' ;
4
18
2025-12-20 17:05:00
5
18
2025-12-21 09:30:00
DELETE FROM "swipes" WHERE "datetime" < '2026-01-01' ;
İki satır gitti. Tarihler YYYY-MM-DD biçiminde metin olduğu için karşılaştırma doğru çalışır.
“Hangi koşul?” tartışması için fırsat: id >= 4 de aynı iki satırı siler ama rastlantıyla. Kimlikler tarih sırasını garanti etmez; silmek istediğimiz şey tarihse koşul tarih olmalı. Metin tarih karşılaştırması yalnızca sabit biçimde çalışır; üçüncü haftadaki datetime TEXT kararının sonucu.
Silinen Kimlik Geri Gelir mi?
En büyük kimlik silindi: 13 gitti
Yine 13 . SQLite en büyük mevcut kimliğe bir ekler.
Aradaki kimlik silindi: 12 gitti, 13 duruyor
14 . Boşluk dolmaz.
Silinen kimliğin yeniden verilmesi istenmiyorsa INTEGER PRIMARY KEY AUTOINCREMENT kullanılır; o zaman sayaç geri dönmez.
Canlı deneyin: Deneme Durağı’nı (13) silip yeniden ekleyince 13 gelir; Yeni Mahalle’yi (12) silip yeniden ekleyince 14 gelir. Öğrenciler “silinen kimlik asla geri gelmez” sanır; SQLite’ta bu yalnızca AUTOINCREMENT ile doğrudur. AUTOINCREMENT ek bir sayaç tablosu tutar; gerekmedikçe kullanmayın, SQLite belgeleri de böyle der.
Uygulama 2: Hangi Koşul?
İki dakika: 17 numaralı kartın yalnızca yükleme (topup) işlemlerini silmek istiyoruz. Önce seçen SELECT’i, sonra DELETE’i yazın. Henüz çalıştırmayın.
SELECT * FROM "swipes" WHERE "card_id" = 17 AND "type" = 'topup' ;
DELETE FROM "swipes" WHERE "card_id" = 17 AND "type" = 'topup' ;
Bir satır seçilir: 1 numaralı yükleme.
İki koşulu AND ile bağlamayı unutanlar bütün yüklemeleri siler. Çalıştırmamalarını istiyoruz; bu yükleme tetikleyici bölümünde işe yarayacak. İsteyen SELECT kısmını çalıştırıp satır sayısını görsün.
Silmek Her Zaman Serbest mi?
Deneme Durağında bir biniş kaydedilmiş olsun. Şimdi durağı silmeye çalışalım:
stations
12
Yeni Mahalle
13
Deneme Durağı
⇄ station_id → id
FOREIGN KEY constraint failed İki adımda silindi
Canlı akış: önce INSERT INTO "swipes" ... VALUES (18, 13, 'boarding', '2026-09-03 10:00:00', -3900); ile binişi ekleyin, sonra DELETE FROM "stations" WHERE "name" = 'Deneme Durağı'; hata verir. Hata almayan öğrenci PRAGMA foreign_keys = ON; yazmamıştır; bu anı yakalayın. Yabancı anahtar olmasaydı 13 numaralı durak silinir, biniş kaydı var olmayan bir durağı gösterirdi.
İki Adımda Silmek
Önce bağlı satırları, sonra durağı sileriz. Durağın kimliğini ezberlemek yerine alt sorguyla buluruz:
DELETE FROM "swipes"
WHERE "station_id" = (
SELECT "id" FROM "stations" WHERE "name" = 'Deneme Durağı'
);
DELETE FROM "stations" WHERE "name" = 'Deneme Durağı' ;
Sıra önemli: ters sırada ilk ifade yine hata verir.
Alt sorgu ikinci haftadan. Öğrenciye “neden önce swipes?” diye sorun: bağlantının ucu silinmeden kaynağı silinemez. Bu iki adım her seferinde elle yazılır; bir sonraki slayt bunu şemaya yazmanın yolunu gösterir.
ON DELETE: Silmeye Verilecek Tepki
Yabancı anahtar tanımına, işaret edilen satır silinince ne olacağı eklenebilir:
FOREIGN KEY ("station_id" ) REFERENCES "stations" ("id" ) ON DELETE SET NULL
RESTRICT
Silme reddedilir. Az önce gördüğümüz davranış.
NO ACTION
Varsayılan; SQLite’ta pratikte RESTRICT gibi davranır.
SET NULL
Yabancı anahtar NULL olur; alt satır kalır.
SET DEFAULT
Yabancı anahtar sütunun varsayılan değerini alır.
CASCADE
Alt satırlar da silinir.
Bu seçenekler ALTER TABLE ile sonradan eklenemez; tablo yeniden oluşturulur. lecture4-schema-v2.sql dosyası bu seçenekleri içeren şemadır, ayrı bir boş veritabanında denenebilir. Sonraki iki slayt SET NULL ve CASCADE’i görselleştirir; hangisinin uygun olduğu bir sonraki slaytta tartışılır.
SET NULL: Kayıt Kalır, Bağ Kopar
Durak kapandı ama biniş kayıtları muhasebe için gerekli. Durağı silince bağlı satırların station_id değeri NULL olur:
stations
1
OMÜ Rektörlük
13
Deneme Durağı
swipes
1
17
1
2
17
13 NULL
3
18
13 NULL
station_id sütunu NULL kabul ettiği için bu seçenek mümkün; yükleme işlemlerinde zaten NULL kullanıyorduk.
Bu, bizim şemamız için en mantıklı seçenek: parasal kayıt silinmez, yalnızca durağa olan bağ kalkar. NOT NULL olan bir sütunda SET NULL seçilemez; card_id için bu yol kapalı. Değerler lecture4-schema-v2.sql ile ayrı bir veritabanında doğrulandı.
CASCADE: Zincirleme Silme
Bir kart iptal edildi ve hareketleri de gitmeli. card_id için ON DELETE CASCADE seçersek kartı silmek hareketlerini de siler:
swipes
1
17
1
4
18
2
5
18
1
Tek DELETE, iki tabloda silme. Kolay ama tehlikeli: bir satır silmek yüzlerce satırı götürebilir.
“Kartı iptal ettik diye yolculuk geçmişini silmek doğru mu?” sorusunu açın; muhasebe, şikâyet ve istatistik için kayıtlar gerekebilir. Teknik olarak CASCADE doğru çalışır; iş kuralı olarak çoğu zaman yanlış tercihtir. Seçim şemada verilir, her DELETE’te değil.
Hangisini Seçmeli?
swipes.station_id → stations
SET NULL
Durak kapanabilir; parasal kayıt kalmalı.
swipes.card_id → cards
RESTRICT
Hareketi olan kart silinmemeli; iptal için başka yol gerekir.
İki kararı da veren şey teknik değil, iş kuralı . Üçüncü haftadaki kısıtlar gibi ON DELETE de şemanın bir parçasıdır.
Kart iptali için “başka yol” dersin sonundaki yumuşak silme olacak. lecture4-schema-v2.sql dosyası ders için CASCADE’i kart bağında gösteriyor; bu slayttaki tabloyla kasıtlı olarak çelişiyor. “Dosyadaki seçimi neden değiştirirdiniz?” diye sorun.
UPDATE: Değeri Değiştirmek
Üçüncü haftada “Türkiş” adını “Türk-İş” yapmak için kaç satıra dokunmamız gerektiğini sormuştuk. Artık durak adı tek yerde:
stations
2
Yurtlar
Atakum
3
Türkiş Türk-İş
Atakum
4
Mimarsinan
Atakum
UPDATE "stations"
SET "name" = 'Türk-İş'
WHERE "name" = 'Türkiş';
UPDATE tablo SET sütun = değer WHERE koşul; Birden fazla sütun virgülle ayrılarak aynı ifadede değiştirilebilir.
Üçüncü haftanın güncelleme anomalisi burada çözülüyor: tek satır. swipes tablosundaki station_id = 3 değerleri aynen kalır; kimlikle bağlamanın karşılığı budur. WHERE olmadan UPDATE bütün satırları değiştirir; bir sonraki slayt.
WHERE Olmadan UPDATE
stations
6
Cumhuriyet Meydanı
İlkadım Atakum
8
Gar
İlkadım Atakum
10
Tekkeköy
Tekkeköy Atakum
UPDATE "stations"
SET "district" = 'Atakum';
WHERE yok: her satır değişir
Silmede olduğu gibi: önce aynı koşulla SELECT, satır sayısına bak, sonra UPDATE.
Bunu da çalıştırmayın. Koşulsuz UPDATE’in bilinçli kullanımı da var: az sonra bütün anket yanıtlarını temizlerken her satırı değiştirmek isteyeceğiz. Fark, niyet.
Alt Sorguyla Güncelleme
3 numaralı iade işlemi yanlış durağa yazılmış; aslında Mimarsinan’da yapıldı. Durağın kimliğini ezberlemeden düzeltelim:
UPDATE "swipes"
SET "station_id" = (
SELECT "id" FROM "stations" WHERE "name" = 'Mimarsinan'
)
WHERE "id" = 3 ;
SET tarafında da WHERE tarafında da alt sorgu kullanılabilir. İkinci haftanın aracı üçüncü işte.
WHERE "id" = 3 satırı olmadan bütün işlemler Mimarsinan’a taşınırdı; bir önceki slaytın uyarısı. Alt sorgunun tek değer döndürmesi gerekir; name sütunu UNIQUE olduğu için burada güvenli.
Uygulama 3: Adı Düzeltin
Bir dakika: Büyük Cami durağının ilçesi yanlışlıkla İlkadım yerine Ilkadim girilmiş olsun. Düzelten UPDATE’i yazın; önce seçen SELECT’i.
SELECT * FROM "stations" WHERE "district" = 'Ilkadim' ;
UPDATE "stations"
SET "district" = 'İlkadım'
WHERE "district" = 'Ilkadim' ;
Koşul name üzerinden de kurulabilir; iki yol da tek satırı seçer.
Öğrencilerin veritabanında bu hata yok; kâğıt üzerinde yazsınlar. İsteyen önce hatayı kendi eliyle oluşturup sonra düzeltebilir. Bu küçük senaryo bir sonraki bölüme geçiş: elle girilmiş veride bu hatalar tek tek değil, onlarca gelir.
Elle Girilmiş Veri Dağınık Gelir
SAMULAŞ bir anket yapmış: “En çok kullandığınız durak hangisi?” Yanıtlar serbest metin olarak yazılmış ve anket.csv dosyasında geldi.
.import --csv anket.csv anket
SELECT "durak" , COUNT (* ) AS "oy" FROM "anket"
GROUP BY "durak" ORDER BY "oy" DESC ;
Yurtlar
2
Mimarsinan
2
Gar
2
yurtlar
1
mimar sinan
1
…
…
20 yanıt, 17 farklı grup . Beş durak için beş grup bekliyorduk.
Bu CSV’de başlık satırı var ve tablo yok; .import tabloyu kendisi kurar, bu yüzden --skip yok. Sonucun tamamını canlı gösterin: ” gar” başında boşluk, “GAR” büyük harf, “Mimarsınan” yazım hatası, “Deniz Evleri” boşluklu. Her satır bir kişinin klavyesinden çıkmış; veri temizliği bu dersin en gerçekçi işi.
Temizlik Dört Adımda
trim()15 baştaki ve sondaki boşluklar gitti
upper()8 büyük küçük harf farkı gitti
LIKE ile düzeltme5 yazım hataları gitti
Her adım bir UPDATE. Hedef: beş durak, beş grup.
Sayılar anket.csv üzerinde doğrulandı. Bu slaytta yalnızca planı verin; komutlar sırayla sonraki slaytlarda. Her adımdan sonra GROUP BY sorgusunu yeniden çalıştırmak ilerlemeyi somutlaştırır; ok tuşuyla önceki sorguya dönmeyi gösterin.
Adım 1: trim ile Boşlukları Atmak
" gar" ve "Gar " farklı değerler; görünmeyen boşluk gruplandırmayı bozar. trim() baştaki ve sondaki boşlukları atar:
UPDATE "anket" SET "durak" = trim ("durak" );
Koşul yok, çünkü her satırı temizlemek istiyoruz. Bu kez koşulsuz UPDATE tam yerinde.
SELECT COUNT(DISTINCT "durak") FROM "anket"; → 17’den 15 ’e.
trim, bir değer alıp değiştirilmiş bir değer döndüren SQLite fonksiyonlarından; SQLite bunlara skaler fonksiyon der. SET "durak" = trim("durak") ifadesi “her satırda sütunun kendi değerinin kırpılmış hâlini yaz” demektir. İçteki boşluklara dokunmaz: “mimar sinan” olduğu gibi kalır.
Adım 2: upper ile Harfleri Eşitlemek
"Gar", "gar" ve "GAR" aynı durak. Hepsini büyük harfe çevirirsek fark kalmaz:
UPDATE "anket" SET "durak" = upper ("durak" );
GAR
5
YURTLAR
4
MIMARSINAN
3
ATAKENT
3
DENIZEVLERI
2
MIMARSıNAN
1
MIMAR SINAN
1
DENIZ EVLERI
1
8 grup kaldı. lower() aynı işi küçük harfle yapar.
Tablodaki “MIMARSıNAN” satırına dikkat çekin: küçük ı olduğu gibi kaldı. Bir sonraki slayt bunu açıklıyor. Anket verisi bilerek Türkçe karaktersiz durak adlarıyla hazırlandı; gerçek veride bu sorun hemen çıkar.
Türkçe Uyarısı: upper Yalnızca ASCII Bilir
SELECT upper ('omü rektörlük' ), lower ('İLKADIM' );
SQLite’ın upper ve lower fonksiyonları yalnızca İngiliz alfabesindeki harfleri çevirir. ö, ı, İ, ş olduğu gibi kalır.
Türkçe metinde harf eşitlemek için ya uygulama tarafında dönüştürün ya da eşleştirmeyi LIKE ve replace() ile kurun.
Bu, ders kitaplarında geçmeyen ama Türkiye’de her projede karşılaşılan bir tuzak. replace("durak", 'ı', 'i') gibi adımlarla kısmen aşılabilir; ICU eklentili SQLite derlemeleri Unicode destekler ama standart dağıtımda yoktur. Öğrencilere “Türkçe karakterli sütunlarda upper ile grup saymayın” cümlesini not ettirin.
Adım 3: Yazım Hatalarını LIKE ile Yakalamak
Kalan farklar yazım hatası: MIMAR SINAN, MIMARSıNAN, DENIZ EVLERI. Tek tek düzeltmek yerine örüntüyle yakalarız:
UPDATE "anket" SET "durak" = 'MIMARSINAN' WHERE "durak" LIKE 'mimar%' ;
UPDATE "anket" SET "durak" = 'DENIZEVLERI' WHERE "durak" LIKE 'deniz%' ;
MIMARSINAN
5
GAR
5
YURTLAR
4
DENIZEVLERI
3
ATAKENT
3
Beş grup, 20 oy. LIKE büyük küçük harfe bakmadığı için MIMARSıNAN da yakalandı.
Örüntü seçimi dikkat ister: 'm%' yazsaydık başka bir durak da yakalanabilirdi. Bu veri kümesinde 'mimar%' yalnızca bu durağı yakalar; büyük veri kümesinde önce aynı LIKE ile SELECT çalıştırıp neyin eşleştiğine bakın. Tek tek düzeltme de meşru: WHERE "durak" = 'MIMAR SINAN'.
Uygulama 4: Örüntüyü Siz Seçin
Bir dakika: Anket tablosunda ATAKENT için "atakent " ve "Atakent" gibi yanıtlar olsaydı ve trim, upper yapılmamış olsaydı, tek UPDATE ile hepsini ATAKENT yapan ifadeyi yazın.
UPDATE "anket" SET "durak" = 'ATAKENT' WHERE "durak" LIKE '%atakent%' ;
Başta ve sonda % olduğu için boşluklu yanıtlar da eşleşir; LIKE harf büyüklüğüne bakmaz.
'%atakent%' örüntüsünün tehlikesi: adında “atakent” geçen başka bir durak varsa o da değişir. Öğrencilerden önce SELECT ... WHERE "durak" LIKE '%atakent%' ile eşleşenleri saymalarını isteyin.
Mola
On dakika ara.
Dönüşte: bir yazma işleminin başka bir yazma işlemini otomatik tetiklemesi ve silmeden silmek.
Tetikleyici: Bir Yazma Diğerini Başlatır
Kartın bakiyesini her işlemden sonra elle güncellemek zorunda mıyız? Tetikleyici, bir tabloya yazıldığında başka bir ifadeyi kendiliğinden çalıştırır:
swipes
3
17
refund
900
8
17
topup
10000
⚡ AFTER INSERT ON swipes
Bakiye sütunu henüz yok; bir sonraki slaytta ALTER TABLE ile eklenecek. Burada yalnızca fikir: biz swipes tablosuna yazdık, cards tablosu kendiliğinden değişti. “Bu UPDATE’i kim yazdı?” sorusunun cevabı: biz, ama bir kez, tetikleyicinin içine.
Önce Bakiye Sütunu
Üçüncü haftanın ALTER TABLE komutuyla cards tablosuna bakiye ekleyelim:
ALTER TABLE "cards" ADD COLUMN "balance_kurus" INTEGER NOT NULL DEFAULT 0 ;
Var olan satırlar varsayılan değeri aldı. Bakiye kuruş cinsinden tam sayı; para için üçüncü haftada verdiğimiz karar.
NOT NULL bir sütun eklerken DEFAULT zorunludur; aksi hâlde var olan satırlar için değer bulunamaz. Bakiyeyi swipes üzerinden SUM ile her seferinde hesaplamak da mümkün; sütunda tutmak okuma hızı için bir tasarım tercihi. Tetikleyici bu tercihi tutarlı kılacak.
CREATE TRIGGER Sözdizimi
CREATE TRIGGER "balance_update"
AFTER INSERT ON "swipes"
FOR EACH ROW
BEGIN
UPDATE "cards"
SET "balance_kurus" = "balance_kurus" + NEW ."amount_kurus"
WHERE "id" = NEW ."card_id" ;
END ;
AFTER INSERT ON "swipes"
Ne zaman: swipes tablosuna ekleme yapıldıktan sonra
FOR EACH ROW
Eklenen her satır için bir kez
BEGIN ... END
Çalışacak ifadeler; birden fazla olabilir
NEW."amount_kurus"
Az önce eklenen satırın değeri
BEFORE ve AFTER seçenekleri var; UPDATE OF sütun ON tablo ve DELETE ON tablo için de yazılabilir. NEW eklenen ya da güncellenen satırı, OLD silinen ya da güncellenmeden önceki satırı temsil eder. Tetikleyici .schema çıktısında tablosunun altında görünür.
Tetikleyiciyi Çalıştırmak
Bir yükleme, bir biniş ekleyelim ve cards tablosuna bakalım:
INSERT INTO "swipes" ("card_id" , "station_id" , "type" , "datetime" , "amount_kurus" )
VALUES (17 , NULL , 'topup' , '2026-09-05 07:30:00' , 10000 );
INSERT INTO "swipes" ("card_id" , "station_id" , "type" , "datetime" , "amount_kurus" )
VALUES (17 , 2 , 'boarding' , '2026-09-05 08:10:00' , - 3900 );
Biz yalnızca swipes tablosuna yazdık; cards iki kez kendiliğinden değişti. Eksi tutar eklemek bakiyeyi düşürür; CHECK kuralı biniş tutarının eksi olmasını zaten garanti ediyor. Tetikleyici yazılmadan önce eklenen işlemler bakiyeye yansımadı; bunu sormak güzel bir tartışma açar.
İkinci Tetikleyici: Silmeden Önce Arşivle
Kapanan durakların adını bir arşiv tablosunda saklayalım. BEFORE DELETE ile silinen satıra OLD üzerinden ulaşırız:
CREATE TABLE "closed_stations" ("id" INTEGER PRIMARY KEY , "name" TEXT NOT NULL ,
"closed_at" TEXT NOT NULL DEFAULT CURRENT_TIMESTAMP );
CREATE TRIGGER "archive_station"
BEFORE DELETE ON "stations"
FOR EACH ROW
BEGIN
INSERT INTO "closed_stations" ("name" ) VALUES (OLD ."name" );
END ;
DELETE FROM "stations" WHERE "name" = 'Yeni Mahalle'; sonrasında arşivde bir satır: Yeni Mahalle, silinme zamanıyla.
OLD."name" silinmekte olan durağın adı. Silme gerçekleşmeden önce arşive yazıldığı için silme yabancı anahtar yüzünden reddedilse bile arşiv satırı kalır; bunu AFTER DELETE ile çözebilirsiniz, iyi bir soru. Bu tetikleyici, “silmeden silmek” fikrine köprü.
Yumuşak Silme: Satırı Silmeden İşaretlemek
Durak kapandı ama kaydı silmek istemiyoruz: geçmiş binişler ona bağlı, raporlarda adı geçmeli. Satırı silmek yerine işaretleriz :
stations
9
Büyük Cami
0
10
Tekkeköy
0
11
Belediye Evleri
0 1
UPDATE "stations" SET "closed" = 1
WHERE "name" = 'Belediye Evleri';
SELECT * FROM "stations"
WHERE "closed" = 0;
Önce sütun gerekir: ALTER TABLE "stations" ADD COLUMN "closed" INTEGER NOT NULL DEFAULT 0;. Sonra slayttaki UPDATE ve SELECT. Yumuşak silme, DELETE’in geri alınamazlığına karşı yaygın bir çözümdür; bedeli, her okuma sorgusuna koşul eklemeyi unutmamak. Gelecek hafta bu koşulu bir görünümün içine saklayacağız.
Silmek mi, İşaretlemek mi?
Kalıcı silme: DELETE Veri gerçekten gider. Yer açılır, geçmiş kaybolur. Kişisel verinin silinmesi istendiğinde çoğu zaman gereken budur.
Yumuşak silme: işaret sütunu Veri kalır, görünmez olur. Geri alınabilir, geçmiş korunur. Her sorguda filtre gerekir.
Durak için işaretlemek doğru. Bir yolcunun kişisel bilgisi için karar hukuki: KVKK’da veri sahibi silinmesini isteyebilir ve “görünmez” yapmak silmek değildir.
Burada teknik değil etik ve hukuki bir karar var. Kişisel Verilerin Korunması Kanunu kapsamında silme talebi geldiğinde yumuşak silme yetmez. Durak, albüm, ürün gibi kişisel olmayan kayıtlarda yumuşak silme rahatça kullanılır. “Hangi tablolarımız kişisel veri taşıyor?” sorusunu sorun; cards tablosu kişiselleştirilmiş kart tutuyorsa cevap evet.
Alıştırmalar
Zaman sınırlıysa 2, 3 ve 5 numaralı soruları sınıfta yapın; 1 ve 4 ders sonrasına kalabilir. Her soruda “bu ifade kaç satıra dokunur?” diye sorun; yazma işlemlerinde ilk refleks bu olmalı.
Soru 1: Yeni Kart, İlk Yükleme
19 numaralı yeni bir kart kaydedin ve 5.000 kuruşluk ilk yüklemesini tek INSERT ile swipes tablosuna yazın. Yükleme durağa bağlı değildir.
INSERT INTO "cards" ("id" ) VALUES (19 );
INSERT INTO "swipes" ("card_id" , "station_id" , "type" , "amount_kurus" )
VALUES (19 , NULL , 'topup' , 5000 );
datetime yazılmadı; varsayılan zaman damgası girer. Tetikleyici kurulduysa kartın bakiyesi 5000 olur.
Soru 2: Bu İfadede Ne Yanlış?
Ekip yalnızca 18 numaralı kartın işlemlerini silmek istiyor:
DELETE FROM "swipes"
WHERE "card_id" = 18
OR "type" = 'boarding' ;
Kaç satır gider? Düzeltin.
OR yüzünden bütün binişler de silinir. Koşul AND olmalıydı ya da yalnızca "card_id" = 18. Yazmadan önce aynı koşulla SELECT COUNT(*) çalıştırın.
Soru 3: Sıra Önemli
Türk-İş durağını kalıcı olarak silmek istiyoruz; durağa bağlı biniş kayıtları var ve şemada ON DELETE tanımı yok. Gereken ifadeleri doğru sırayla yazın.
DELETE FROM "swipes"
WHERE "station_id" = (SELECT "id" FROM "stations" WHERE "name" = 'Türk-İş' );
DELETE FROM "stations" WHERE "name" = 'Türk-İş' ;
Önce bağlı satırlar, sonra durak. Şemada ON DELETE SET NULL olsaydı tek ifade yeterdi ve biniş kayıtları kalırdı.
Soru 4: Ankette Son Düzeltme
anket tablosunda "YURTLAR" için bir de "YURTLAR DURAGI" yanıtı olsaydı, bunu diğerleriyle aynı gruba sokan tek UPDATE nedir? Örüntü neden tehlikeli olabilir?
UPDATE "anket" SET "durak" = 'YURTLAR' WHERE "durak" LIKE 'yurtlar%' ;
Tehlike: yurtlar ile başlayan başka bir durak adı varsa o da değişir. Önce aynı LIKE ile SELECT.
Soru 5: Tetikleyiciyi Tamamlayın
Bir işlem silinince kart bakiyesi de düzelmeli. Boşlukları doldurun:
CREATE TRIGGER "balance_revert"
AFTER DELETE ON "swipes"
FOR EACH ROW
BEGIN
UPDATE "cards"
SET "balance_kurus" = "balance_kurus" ___ ___."amount_kurus"
WHERE "id" = ___."card_id" ;
END ;
SET "balance_kurus" = "balance_kurus" - OLD ."amount_kurus"
WHERE "id" = OLD ."card_id" ;
Silinen satıra OLD ile ulaşılır; eklenen tutar geri çıkarılır.
Bu Dersin Anahtar Kelimeleri
Satır ekleme
INSERT INTO ... VALUES, INSERT INTO ... SELECT
Dosyadan aktarma
.import --csv --skip 1
Satır silme
DELETE FROM ... WHERE
Silmeye tepki
ON DELETE RESTRICT / SET NULL / CASCADE
Değer değiştirme
UPDATE ... SET ... WHERE
Temizlik
trim(), upper(), lower(), LIKE
Otomatik ifade
CREATE TRIGGER ... BEGIN ... END, NEW, OLD
Yumuşak silme
İşaret sütunu + UPDATE + filtreli SELECT
Çıkış Soruları
Üç kısa yanıt yazın:
INSERT yazarken kimliği ne zaman biz veririz, ne zaman SQLite’a bırakırız?
DELETE FROM "stations"; ile DROP TABLE "stations"; arasındaki fark nedir?
Aşağıdaki ifade kaç satırı değiştirir ve bu bir sorun mudur?
UPDATE "anket" SET "durak" = trim ("durak" );
Bir dakika bireysel süre. Üçüncü sorunun cevabı “hepsini, ve bu niyetli” olmalı; koşulsuz yazmanın her zaman hata olmadığını yoklar.
Kataloğa Dönüş
Dış dünyada basılı bir numara varsa biz veririz: kart numarası. Satırı yalnızca ayırt edecek bir sayı gerekiyorsa SQLite’a bırakırız: durak kimliği.
DELETE satırları siler, tablo ve şema kalır. DROP TABLE tabloyu yapısıyla kaldırır.
Bütün satırları değiştirir ve bu niyetlidir: her yanıtın boşluklarını temizlemek istiyoruz.
Bugün şemayı veriyle doldurduk, yanlışları düzelttik, sildik ve bir yazmanın başka bir yazmayı tetiklemesini sağladık. Gelecek hafta filtreli sorguları görünüm olarak saklayacağız.
Kaynaklar ve Dosyalar
SQLite: INSERT , DELETE , UPDATE , CREATE TRIGGER , foreign key eylemleri , komut satırı ve .import , skaler fonksiyonlar .
Silberschatz, Korth ve Sudarshan, Database System Concepts , 7. baskı: Bölüm 3.9 (veri değiştirme), Bölüm 4.4 (bütünlük kısıtları), Bölüm 5.3 (tetikleyiciler).
Veri yazma akışı CS50 SQL’in üçüncü haftasından uyarlandı; örnekler SAMULAŞ şemasına taşındı.
Ders dosyaları: schema.sql , schema-v2.sql , duraklar.csv , yeni-duraklar.csv , anket.csv .
Durak adları SAMULAŞ hattından; ilçe bilgileri, kart hareketleri ve anket yanıtları ders için kurgulandı. Tüm çıktılar SQLite 3.51 ile doğrulandı.