← Kursa Dön
📄 Text · 30 min

DELETE vs TRUNCATE vs DROP Farkları

Giriş — Üç Farklı "Silme" Komutu

SQL'de bir şeyleri silmek istediğinde karşına üç farklı komut çıkar: DELETE, TRUNCATE ve DROP. Hepsi bir şeyleri "siler" ama yapıları ve sonuçları çok farklıdır. Bu farkları bilmemek ciddi sorunlara yol açabilir.

🎯 Analoji: Bir ev düşün. - DELETE → Evdeki belirli eşyaları çöpe atarsın (istediğin eşyaları seçebilirsin) - TRUNCATE → Evin içini komple boşaltırsın (tüm eşyalar gider ama ev duruyor) - DROP → Evi yıkarsın (ev de, eşyalar da, her şey gider)


DELETE — Satır Bazında Silme

DELETE DML komutudur ve tablodaki belirli satırları siler.

Temel Sözdizimi

DELETE FROM tablo_adi 
WHERE kosul;

Koşullu Silme

-- Belirli bir müşteriyi sil
DELETE FROM customers WHERE customer_id = 11;
-- Query OK, 1 row affected

-- Aktif olmayan müşterileri sil
DELETE FROM customers WHERE is_active = FALSE;

-- Belirli tarihten önceki siparişleri sil
DELETE FROM orders WHERE order_date < '2023-01-01' AND status = 'cancelled';

-- Stoku 0 ve aktif olmayan ürünleri sil
DELETE FROM products WHERE stock_quantity = 0 AND is_active = FALSE;

WHERE Olmadan DELETE — Tüm Satırları Sil

-- ⚠️ TÜM satırları siler!
DELETE FROM order_items;
-- Tüm sipariş kalemleri silindi

-- Bu, TRUNCATE'e benzer ama çok daha yavaş (her satırı tek tek siler)

DELETE ile LIMIT

-- En eski 10 iptal edilmiş siparişi sil
DELETE FROM orders 
WHERE status = 'cancelled' 
ORDER BY order_date ASC 
LIMIT 10;

DELETE ve Foreign Key

Foreign key ilişkisi olan tablolarda silme işlemi kısıtlanabilir:

-- Müşteri silmeye çalışalım (ama bu müşterinin siparişleri var)
DELETE FROM customers WHERE customer_id = 1;
-- ERROR: Cannot delete or update a parent row: a foreign key constraint fails

-- Önce child kayıtları silmelisin:
-- 1. Sipariş kalemlerini sil
DELETE FROM order_items WHERE order_id IN (
    SELECT order_id FROM orders WHERE customer_id = 1
);
-- 2. Siparişleri sil
DELETE FROM orders WHERE customer_id = 1;
-- 3. Artık müşteriyi silebilirsin
DELETE FROM customers WHERE customer_id = 1;

Eğer ON DELETE CASCADE tanımlıysa, parent silindiğinde child'lar otomatik silinir:

-- orders → order_items ilişkisinde CASCADE varsa:
DELETE FROM orders WHERE order_id = 1;
-- order_items'taki order_id = 1 olan kayıtlar da otomatik silinir

DELETE'in Özellikleri

ÖzellikDELETE
TürDML
WHERE desteği✅ Evet
Transaction/ROLLBACK✅ Geri alınabilir
AUTO_INCREMENT sıfırlama❌ Hayır
Trigger tetikleme✅ Evet
Foreign key kontrolü✅ Evet
Hız (büyük tablolar)🐢 Yavaş (satır satır)
LogHer satır loglanır

TRUNCATE — Tablonun Tüm İçeriğini Temizle

TRUNCATE DDL komutudur ve tablodaki tüm satırları bir anda siler. WHERE koşulu kullanılamaz.

Sözdizimi

TRUNCATE TABLE tablo_adi;

Kullanım

-- Tüm sipariş kalemlerini temizle
TRUNCATE TABLE order_items;
-- Tüm satırlar anında silindi

-- Log tablosunu temizle
TRUNCATE TABLE activity_logs;

TRUNCATE'in Özellikleri

ÖzellikTRUNCATE
TürDDL
WHERE desteği❌ Hayır (tüm satırlar silinir)
Transaction/ROLLBACK❌ Geri alınamaz (auto-commit)
AUTO_INCREMENT sıfırlama✅ Evet (1'den başlar)
Trigger tetikleme❌ Hayır
Foreign key kontrolü✅ Evet (FK varsa hata verebilir)
Hız (büyük tablolar)🚀 Çok hızlı
LogMinimal (sadece sayfa deallocate)

TRUNCATE Neden Hızlı?

DELETE FROM tablo her satırı tek tek siler ve her silme işlemini loglar. 10 milyon satırlık bir tabloda bu çok uzun sürer.

TRUNCATE TABLE ise tablonun veri sayfalarını bir bütün olarak serbest bırakır — satırları tek tek silmez. Tablo sanki yeni oluşturulmuş gibi boş hale gelir.

-- 10 milyon satırlık tablo
DELETE FROM huge_table;     -- Dakikalar hatta saatler sürebilir
TRUNCATE TABLE huge_table;  -- Saniyeler içinde tamamlanır

TRUNCATE ve Foreign Key Sorunu

-- Foreign key referansı olan tabloyu TRUNCATE edemezsin
TRUNCATE TABLE customers;
-- ERROR: Cannot truncate a table referenced in a foreign key constraint

-- Çözüm 1: Önce child tabloları temizle
TRUNCATE TABLE order_items;
TRUNCATE TABLE orders;
TRUNCATE TABLE customers;  -- Artık çalışır

-- Çözüm 2: Geçici olarak FK kontrolünü kapat
SET FOREIGN_KEY_CHECKS = 0;
TRUNCATE TABLE customers;
SET FOREIGN_KEY_CHECKS = 1;

⚠️ Dikkat: SET FOREIGN_KEY_CHECKS = 0 ile FK kontrolünü kapatmak, veri tutarsızlığına yol açabilir. Sadece geliştirme ortamında ve ne yaptığından emin olduğunda kullan. Production'da asla kullanma.


DROP — Tabloyu (veya Veritabanını) Tamamen Yok Et

DROP DDL komutudur ve tablonun yapısıyla birlikte her şeyini siler. Tablo artık yok — sütunlar, veriler, indexler, constraint'ler, hepsi.

Sözdizimi

DROP TABLE tablo_adi;
DROP TABLE IF EXISTS tablo_adi;

Kullanım

-- Tabloyu sil
DROP TABLE temp_reports;

-- Güvenli silme (yoksa hata vermez)
DROP TABLE IF EXISTS temp_reports;

-- Birden fazla tabloyu sil
DROP TABLE IF EXISTS temp1, temp2, temp3;

-- Veritabanını sil
DROP DATABASE IF EXISTS test_db;

DROP'un Özellikleri

ÖzellikDROP
TürDDL
Ne silerTablo yapısı + tüm veriler + indexler + constraint'ler
Transaction/ROLLBACK❌ Geri alınamaz
SonuçTablo artık yok, SHOW TABLES'ta görünmez

Üçünün Karşılaştırması

Bu tablo en önemli referans noktanız:

ÖzellikDELETETRUNCATEDROP
SQL türüDMLDDLDDL
Ne silerBelirli satırlarıTüm satırlarıTablo + tüm veriler
WHERE✅ Kullanılabilir❌ Kullanılamaz❌ N/A
Tablo yapısı kalır mı✅ Evet✅ Evet❌ Hayır
ROLLBACK✅ Mümkün❌ Mümkün değil❌ Mümkün değil
AUTO_INCREMENTSıfırlanmazSıfırlanırN/A
Trigger tetikler mi✅ Evet❌ Hayır❌ Hayır
Hız (büyük tablo)🐢 Yavaş🚀 Çok hızlı🚀 Çok hızlı
Disk alanıHemen serbest bırakmayabilirHemen serbest bırakırHemen serbest bırakır
FK kontrolü✅ Kontrol eder✅ Kontrol eder✅ Kontrol eder

Ne Zaman Hangisi?

DELETE kullan:

  • Belirli satırları silmek istediğinde

  • Silme işlemini geri alabilmek istediğinde (transaction)

  • Trigger'ların tetiklenmesi gerektiğinde

  • Log tablosundaki belirli tarihteki kayıtları temizlerken

-- Kullanım: İptal edilen siparişleri temizle
DELETE FROM orders WHERE status = 'cancelled' AND order_date < '2023-01-01';

TRUNCATE kullan:

  • Tablonun tüm verilerini hızlıca silmek istediğinde

  • AUTO_INCREMENT'ı sıfırlamak istediğinde

  • Test/geliştirme ortamında tabloları temizlerken

-- Kullanım: Test verilerini temizle
TRUNCATE TABLE test_orders;

DROP kullan:

  • Tabloya artık hiç ihtiyacın olmadığında

  • Geçici tabloları temizlerken

  • Veritabanı yapısını yeniden oluşturacaksan

-- Kullanım: Geçici tabloyu kaldır
DROP TABLE IF EXISTS temp_bestsellers;

Soft Delete — Silmeden "Silme"

Gerçek dünyada veri nadiren fiziksel olarak silinir. Bunun yerine soft delete (yumuşak silme) yaklaşımı kullanılır:

-- Fiziksel silme (hard delete) — veri gitti, geri gelmez
DELETE FROM customers WHERE customer_id = 5;

-- Yumuşak silme (soft delete) — veri duruyor ama "silinmiş" işaretleniyor
UPDATE customers 
SET is_active = FALSE, 
    deleted_at = NOW() 
WHERE customer_id = 5;

Soft delete'in avantajları:

  • Veri kurtarma mümkün (geri alma = is_active = TRUE yapma)

  • Yasal gereklilikler karşılanır (bazı veriler yasaya göre silinmemeli)

  • Audit trail korunur (kim ne zaman sildi?)

  • Bağlı veriler bozulmaz (foreign key ilişkileri korunur)

-- "Silinmemiş" (aktif) verileri sorgula
SELECT * FROM customers WHERE is_active = TRUE;
-- veya
SELECT * FROM customers WHERE deleted_at IS NULL;

-- "Silinen" kayıtları da dahil et (admin görünümü)
SELECT * FROM customers;

💡 İpucu: Çoğu modern uygulama soft delete kullanır. Laravel'de SoftDeletes trait'i, Django'da is_deleted field'ı yaygın pattern'lardır. Veritabanı seviyesinde is_active veya deleted_at sütunu eklemek yeterlidir.


Gerçek Dünya Örneği — Veri Temizleme Script'i

-- Aylık veri temizleme script'i
-- Her ayın 1'inde çalıştırılır

START TRANSACTION;

-- 1. 6 aydan eski iptal edilmiş siparişlerin kalemlerini sil
DELETE oi FROM order_items oi
JOIN orders o ON oi.order_id = o.order_id
WHERE o.status = 'cancelled' 
  AND o.order_date < DATE_SUB(NOW(), INTERVAL 6 MONTH);

-- Kaç kayıt silindi?
SELECT ROW_COUNT() AS deleted_items;

-- 2. 6 aydan eski iptal edilmiş siparişleri sil
DELETE FROM orders 
WHERE status = 'cancelled' 
  AND order_date < DATE_SUB(NOW(), INTERVAL 6 MONTH);

SELECT ROW_COUNT() AS deleted_orders;

-- 3. Hiç sipariş vermemiş ve 1 yıldan eski müşterileri pasife al (soft delete)
UPDATE customers 
SET is_active = FALSE 
WHERE customer_id NOT IN (SELECT DISTINCT customer_id FROM orders)
  AND registration_date < DATE_SUB(NOW(), INTERVAL 1 YEAR);

SELECT ROW_COUNT() AS deactivated_customers;

-- Her şey doğru görünüyorsa
COMMIT;

Performans Karşılaştırması

10 milyon satırlık bir tabloda:

İşlemYaklaşık SüreAçıklama
DELETE FROM big_table5-30 dakikaHer satır tek tek silinir, loglanır
DELETE ... WHERE id < 100000030-120 saniye1M satır silinir
TRUNCATE TABLE big_table1-5 saniyeSayfa bazlı temizleme
DROP TABLE big_table<1 saniyeDosya silme

DELETE'in yavaş olmasının sebepleri:

  • Her satır için undo log yazılır (ROLLBACK için)

  • Her satır için redo log yazılır (crash recovery için)

  • Index'ler satır bazında güncellenir

  • Trigger'lar her satır için çalışır


Sıkça Yapılan Hatalar

  1. DELETE ile TRUNCATE'i karıştırmak: "Tabloyu temizle" diyorsun ama DELETE kullanıyorsun — milyonlarca satır tek tek siliniyor ve işlem saatler sürüyor. Tüm tabloyu temizleyeceksen TRUNCATE kullan.

  2. WHERE olmadan DELETE: DELETE FROM customers tüm müşterileri siler. SQL_SAFE_UPDATES = 1 ile bunu engelle.

  3. Foreign key sırasını unutmak: Child tabloyu silmeden parent'ı silemezsin. Sıra: önce order_items, sonra orders, sonra customers.

  4. DROP'u geri alabileceğini sanmak: DROP TABLE geri alınamaz. Transaction içinde olsan bile DDL auto-commit yapar.

  5. TRUNCATE'in trigger tetiklemediğini bilmemek: Eğer tablodaki silme işlemlerini loglayan bir trigger varsa, TRUNCATE bu trigger'ı çalıştırmaz. Audit kaybı yaşarsın.

  6. Production'da TRUNCATE yerine DROP kullanmak: Tabloyu boşaltmak istiyordun ama DROP ile tabloyu tamamen sildin. Tablonun yapısını tekrar oluşturman gerekir.


Özet

  • DELETE → DML, belirli satırları siler, WHERE destekler, ROLLBACK yapılabilir, yavaş

  • TRUNCATE → DDL, tüm satırları siler, WHERE yok, ROLLBACK yok, çok hızlı, AUTO_INCREMENT sıfırlar

  • DROP → DDL, tabloyu tamamen yok eder (yapı + veri), geri alınamaz

  • Gerçek uygulamalarda soft delete (is_active = FALSE) tercih edilir

  • Production'da silme işlemlerinden önce her zaman yedek al

  • WHERE olmadan DELETE yazmamak için SQL_SAFE_UPDATES modunu aç

  • Foreign key olan tablolarda silme sırası: child önce, parent sonra

  • Büyük tablolarda toplu silme için TRUNCATE veya batch DELETE kullan