Menu

SQLite DELETE: WHERE ve RETURNING ile Satır Silme

SQLite'ta DELETE komutu nasıl çalışır? Güvenli WHERE kullanımı, tüm satırları silme, ilişkili tablolarda cascade ve RETURNING ile silinen satırları geri alma.

Bu sayfada çalıştırılabilir editörler var — düzenle, çalıştır ve sonucu anında gör.

DELETE Sadece Satır Siler, Başka Bir Şey Değil

DELETE, bir tablodan satır çıkarır. Tabloyu silmez, şemasını değiştirmez, başka tablolara da dokunmaz (tabii cascade ayarlamadıysanız). Söz dizimi gayet kısa:

DELETE FROM users WHERE id = 2; ifadesi, koşula uyan satırları bulur ve onları siler. Diğer iki satır olduğu gibi kalır. Tablonun kendisi hâlâ yerinde duruyor — istediğiniz kadar yeni kayıt eklemeye devam edebilirsiniz.

Olayı şöyle düşünün: DELETE, eşleşen satırları döndürmek yerine çöpe atan bir SELECT gibidir.

Asıl İşi Yapan WHERE Koşulu

Ciddi bir DELETE sorgusunun kaderi, tamamen WHERE koşulunda saklıdır. Doğru yazarsanız tam olarak silmek istediğiniz satırlar gider. Yanlış yazarsanız niyet ettiğinizden çok daha fazlasını — bazen tablonun tamamını — silmiş olursunuz.

Yayınlanmamış ve hiç görüntülenmemiş iki taslak silindi. Yayındaki satırlar ise koşula uymadığı için olduğu gibi duruyor. WHERE ifadesinde aklınıza gelen her şeyi kullanabilirsiniz: IN, LIKE, BETWEEN, alt sorgular, AND/OR kombinasyonları...

Edinmeye değer bir alışkanlık: DELETE çalıştırmadan önce aynı WHERE koşulunu bir de SELECT ile deneyin.

-- Nelerin gideceğini önizleyin:
SELECT * FROM posts WHERE published = 0 AND views = 0;

-- Satırlardan memnun musunuz? Şimdi onları silin:
DELETE FROM posts WHERE published = 0 AND views = 0;

Bu iki adımlık dans, bütün yedekleme araçlarının toplamından daha fazla veritabanını kurtarmıştır.

WHERE Olmadan DELETE Tabloyu Boşaltır

WHERE koşulunu yazmazsan DELETE tüm satırları siler:

Tablo boş ama hâlâ duruyor. SQLite'ta TRUNCATE diye bir komut yok; bunun yerine DELETE FROM tablo; kullanılıyor. SQLite arka planda "truncate optimizasyonu" denilen bir numara çeviriyor: satırları tek tek silmek yerine ilgili sayfaları topluca düşürüyor. Hızlı ama yine de transactional bir işlem, yani gerektiğinde rollback yapabilirsiniz.

Birincil anahtarda AUTOINCREMENT kullandıysan sayaç kendiliğinden sıfırlanmaz. id'leri tekrar 1'den başlatmak istiyorsanız ilgili sequence satırını da temizlemeniz gerekir:

DELETE FROM log;
DELETE FROM sqlite_sequence WHERE name = 'log';

Düz INTEGER PRIMARY KEY (yani AUTOINCREMENT olmadan) kullanıyorsanız SQLite zaten id'leri serbestçe yeniden kullanır; dolayısıyla buna gerek kalmaz.

Birden Fazla Belirli Satırı Silme

Belirli bir satır kümesini silmenin en temiz yolu IN kullanmaktır:

Bir alt sorgu kullanarak da silme işlemi yapabilirsiniz — silinecek satırlar bir join ya da başka bir tablo üzerinden belirleniyorsa bu yöntem çok işine yarar:

SQLite, MySQL'deki gibi DELETE ... JOIN sözdizimini desteklemez; ama WHERE içindeki bir alt sorgu (subquery) aynı işi pekala görür.

RETURNING: Sildiğiniz Satırları Görmek

DELETE sorgusuna RETURNING eklerseniz, silinen satırları tıpkı bir SELECT çalıştırmış gibi geri alırsınız:

Silinen her satırın id ve email değerleri elinize geliyor. Bu da şu durumlarda hayat kurtarır:

  • Tam olarak nelerin silindiğini loglamak.
  • Geri alma (undo) özelliği eklemek — dönen satırları bir kenara stashleyip sonra geri yazabilirsiniz.
  • Silme işleminin beklediğiniz satırları etkileyip etkilemediğini tek seferde, ek sorgu atmadan doğrulamak.

RETURNING; INSERT, UPDATE ve DELETE ifadelerinin hepsinde çalışır. Ayrıntılı anlatımı kendi sayfasında bulabilirsiniz.

İlişkili Satırlar İçin ON DELETE CASCADE

Ana tablo ile alt tablo bir foreign key ile bağlıyken ana tablodaki satırı sildiğinizde alt tabloda öksüz kalan satırlar oluşur — tabii SQLite'a cascade yapmasını söylemediyseniz:

Yazarı silmek, ona ait kitapları da siler. ON DELETE CASCADE olmasaydı aynı DELETE ya başarılı olur ve sahipsiz kitaplar bırakırdı (foreign key kapalıysa) ya da constraint hatasıyla patlardı (açıksa).

Asıl tuzak şu: SQLite'ta foreign key'ler varsayılan olarak kapalıdır. Her bağlantı için PRAGMA foreign_keys = ON; komutunu çalıştırmanız gerekir. Bu pragma ayarlanmadıysa ON DELETE CASCADE sessiz sedasız görmezden gelinir — kitaplar yerinde kalır. Çoğu uygulama sürücüsü bunu sizin yerinize ayarlar veya bir seçenek olarak sunar; kullandığınızı bir kontrol edin.

Bilmeye değer diğer cascade seçenekleri: ON DELETE SET NULL (foreign key'i temizler), ON DELETE RESTRICT (alt kayıtlar varsa silmeyi reddeder), ON DELETE NO ACTION (varsayılan — çoğu durumda RESTRICT ile aynı).

LIMIT ile DELETE (Derleme Zamanı Seçeneği)

Bazı SQLite derlemeleri DELETE ... LIMIT kullanımını destekler; bu da devasa tablolardan parça parça satır silmek için oldukça işe yarar:

DELETE FROM logs
WHERE created_at < '2024-01-01'
ORDER BY created_at
LIMIT 1000;

Bunun çalışması için SQLite'ın SQLITE_ENABLE_UPDATE_DELETE_LIMIT bayrağıyla derlenmiş olması gerekiyor. Resmi binary'lerde ve çoğu dil bağlayıcısında (Python'un sqlite3'ü, Node'un better-sqlite3'ü) bu özellik zaten açık. Sizinkinde açık değilse sözdizimi hatası alırsınız; bu durumda alt sorgu kullanarak şu şekilde halledebilirsiniz:

DELETE FROM logs
WHERE id IN (
    SELECT id FROM logs
    WHERE created_at < '2024-01-01'
    ORDER BY created_at
    LIMIT 1000
);

Toplu silme işlemlerini küçük parçalara bölmek, transaction'ları kısa tutar — başka bağlantılar veritabanını okurken bu çok önemli olur.

Büyük Silme İşlemlerini Transaction İçine Alın

DELETE zaten örtük olarak transaction'lıdır: ya eşleşen tüm satırlar silinir ya da hiçbiri. Ama çok sayıda satır sileceğiniz zaman, işlemi açık bir transaction içine almak size bir avantaj sağlar — bir terslik fark ederseniz ROLLBACK ile geri dönebilirsiniz:

ROLLBACK silme işlemini tamamen geri alır. Gerçek bir oturumda, sayılar doğru göründüğünde COMMIT ile işlemi onaylarsınız. Ayrıca, çok sayıda satırı tek tek silerken transaction kullanmak işi inanılmaz hızlandırır — döngüyü BEGIN/COMMIT arasına almak, her silme için ayrı ayrı fsync yapılmasının önüne geçer.

Silinmeyen Şeyler

Sık karşılaşılan birkaç kafa karışıklığını netleştirelim:

  • DELETE FROM tablo; tabloyu boşaltır ama ortadan kaldırmaz. Tablonun kendisini silmek için DROP TABLE tablo; kullan.
  • DELETE veritabanı dosyasını küçültmez. Boşalan sayfalar yeniden kullanılmak üzere "boş" olarak işaretlenir. Disk alanını geri kazanmak için VACUUM; çalıştır (performans bölümünde anlatılıyor).
  • Bir satırı silmek, başka tablolardaki bağlı satırları otomatik olarak silmez — bunun olması için hem ON DELETE CASCADE tanımlı olmalı hem de foreign key desteği açık olmalı.
  • Hiçbir satıra denk gelmeyen bir DELETE hata değildir. changes() = 0 döndüren başarılı bir ifadedir. Bilmeniz gerekiyorsa etkilenen satır sayısını kontrol et.

Sırada: UPSERT

Çoğu zaman aslında silmek istemezsin — satır yeniyse eklemek, varsa güncellemek istersiniz. SQLite buna UPSERT diyor ve ON CONFLICT ifadesi sayesinde üç ayrı sorgu yerine tek bir ifadeyle hallediyorsunuz. Sıradaki konu bu.

Sıkça Sorulan Sorular

SQLite'ta bir satır nasıl silinir?

Yapı şu şekilde: DELETE FROM tablo_adi WHERE kosul;. WHERE koşulu hangi satırların gideceğini belirler. Mesela DELETE FROM users WHERE id = 7; sadece id'si 7 olan kullanıcıyı siler. WHERE yazmazsanız tablodaki tüm satırlar uçar — dikkat.

SQLite tablosundaki tüm satırları nasıl silerim?

WHERE koşulu olmadan DELETE FROM tablo_adi; çalıştırmanız yeterli. SQLite'ta TRUNCATE ifadesi yok; filtresiz DELETE onun yerine geçer ve SQLite bunu içeride optimize eder (buna 'truncate optimization' denir). AUTOINCREMENT sayaçlarını da sıfırlamak istiyorsanız sonrasında sqlite_sequence tablosundan ilgili kaydı silmeniz gerekir.

SQLite ilişkili tablolara cascade delete yapabilir mi?

Evet, foreign key tanımında ON DELETE CASCADE belirttiyseniz ve PRAGMA foreign_keys = ON; ile foreign key desteğini açtıysanız çalışır. SQLite'ta foreign key'ler varsayılan olarak kapalıdır; bu pragma'yı vermezseniz cascade kuralları sessizce yok sayılır — yani hiçbir hata almazsınız ama silme de yayılmaz.

Hangi satırların silindiğini nasıl görebilirim?

Sorgunun sonuna RETURNING ekleyin: DELETE FROM users WHERE active = 0 RETURNING id, email; silinen satırları aynı bir SELECT gibi geri döndürür. Loglama, geri alma (undo) özellikleri veya tam istediğiniz kayıtları sildiğinizden emin olmak için çok kullanışlıdır.

Coddy programming languages illustration

Coddy ile kodlamayı öğren

BAŞLA