TRUNCATE ile Tablodaki Tüm Verileri Silme

Veritabanı yönetiminde zaman zaman bir tablonun tüm verilerini temizlemeniz gerekir. Belki test ortamında biriken sahte kayıtları sileceksiniz, belki periyodik olarak sıfırlanan bir log tablosu var, ya da bir migration öncesi tabloyu boşaltmanız lazım. Bu işi yapmanın birkaç yolu var ama TRUNCATE komutu, çoğu senaryoda en hızlı ve en temiz çözümü sunar. Bu yazıda TRUNCATE‘i her yönüyle inceleyeceğiz, DELETE ile farkını net olarak ortaya koyacağız ve gerçek dünya senaryolarında nasıl kullanacağınızı göreceğiz.

TRUNCATE Nedir ve Ne İşe Yarar?

TRUNCATE TABLE, bir tablodaki tüm satırları silmek için kullanılan bir DDL (Data Definition Language) komutudur. Bunu duyan birçok kişi “zaten DELETE FROM tablo da aynı şeyi yapmıyor mu?” diye sorar. Teknik olarak sonuç benzer görünse de arka planda çok farklı şeyler olur.

TRUNCATE, tabloyu satır satır silmek yerine tablo verilerini içeren veri sayfalarını doğrudan boşaltır. Bu nedenle çok daha hızlıdır. Özellikle milyonlarca satır içeren tablolarda bu fark çarpıcı biçimde ortaya çıkar.

Temel söz dizimi son derece basittir:

TRUNCATE TABLE tablo_adi;

Ya da TABLE anahtar kelimesini atlayarak:

TRUNCATE tablo_adi;

Her iki kullanım da aynı sonucu verir. MariaDB ve MySQL’de her ikisi de geçerlidir.

DELETE ile TRUNCATE Arasındaki Farklar

Bu iki komut arasındaki farkları anlamak, hangisini kullanacağınıza karar vermenizi sağlar.

TRUNCATE:

  • DDL (Data Definition Language) komutudur
  • Transaction log’a satır bazında yazım yapmaz, bu yüzden çok daha hızlıdır
  • WHERE koşulu kullanamaz, tabloyu tamamen boşaltır
  • AUTO_INCREMENT sayacını sıfırlar
  • Geri alınamaz (bazı storage engine’lerde)
  • Trigger’ları tetiklemez
  • Foreign key constraint’i olan tablolarda doğrudan kullanılamaz

DELETE:

  • DML (Data Manipulation Language) komutudur
  • Her satır için transaction log’a kayıt yazar
  • WHERE koşuluyla belirli satırları silebilirsiniz
  • AUTO_INCREMENT sayacını sıfırlamaz
  • Transaction içinde geri alınabilir (ROLLBACK)
  • DELETE trigger’larını tetikler
  • Foreign key constraint’i olan tablolarda çalışabilir

Şimdi bu farkları gösteren somut örneklere bakalım.

AUTO_INCREMENT Davranışı

-- Önce test tablosu oluşturalım
CREATE TABLE test_kullanicilar (
    id INT AUTO_INCREMENT PRIMARY KEY,
    ad VARCHAR(100),
    email VARCHAR(150)
);

-- Birkaç kayıt ekleyelim
INSERT INTO test_kullanicilar (ad, email) VALUES
('Ahmet Yilmaz', '[email protected]'),
('Mehmet Demir', '[email protected]'),
('Ayse Kaya', '[email protected]');

-- Şu anki AUTO_INCREMENT değerini kontrol edelim
SHOW TABLE STATUS LIKE 'test_kullanicilar';

Bu noktada AUTO_INCREMENT değeri 4 olacaktır. Şimdi DELETE ile silelim:

-- DELETE ile silme
DELETE FROM test_kullanicilar;

-- Yeni kayıt ekleyelim
INSERT INTO test_kullanicilar (ad, email) VALUES ('Yeni Kullanici', '[email protected]');

-- ID'ye bakalım - 4'ten devam edecek!
SELECT * FROM test_kullanicilar;

Şimdi aynı tabloyu TRUNCATE ile temizleyelim:

-- Tekrar kayıt ekleyelim
INSERT INTO test_kullanicilar (ad, email) VALUES
('Ahmet Yilmaz', '[email protected]'),
('Mehmet Demir', '[email protected]');

-- TRUNCATE ile temizle
TRUNCATE TABLE test_kullanicilar;

-- Yeni kayıt ekleyelim
INSERT INTO test_kullanicilar (ad, email) VALUES ('Yeni Kullanici', '[email protected]');

-- ID 1'den başlayacak!
SELECT * FROM test_kullanicilar;

TRUNCATE sonrası eklenen ilk kaydın ID’si 1 olacaktır. Bu davranış, özellikle sequential ID önemli olan senaryolarda kritiktir.

Gerçek Dünya Senaryoları

Senaryo 1: Log Tablosunu Periyodik Temizleme

Bir uygulama log tablosu düşünün. Her gün binlerce hatta milyonlarca satır birikiyor ve belirli aralıklarla temizlemeniz gerekiyor. Belki son 30 günden eski logları bir archive tablosuna taşıyıp, ana log tablosunu temizliyorsunuz.

-- Önce eski logları archive tablosuna taşı
INSERT INTO uygulama_log_archive
SELECT * FROM uygulama_log
WHERE log_tarihi < DATE_SUB(NOW(), INTERVAL 30 DAY);

-- Ardından ana tabloyu temizle
TRUNCATE TABLE uygulama_log;

Bu işlemi bir shell script içinde otomatikleştirmek istiyorsanız:

#!/bin/bash
# log_temizle.sh

DB_HOST="localhost"
DB_USER="dbadmin"
DB_PASS="guvenli_sifre"
DB_NAME="uygulama_db"
LOG_FILE="/var/log/db_temizlik.log"

echo "[$(date '+%Y-%m-%d %H:%M:%S')] Log arşivleme başladı" >> $LOG_FILE

# Eski logları arşive taşı
mysql -h $DB_HOST -u $DB_USER -p$DB_PASS $DB_NAME -e "
    INSERT INTO uygulama_log_archive
    SELECT * FROM uygulama_log
    WHERE log_tarihi < DATE_SUB(NOW(), INTERVAL 30 DAY);
" 2>> $LOG_FILE

if [ $? -eq 0 ]; then
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] Arşivleme başarılı, tablo temizleniyor" >> $LOG_FILE
    mysql -h $DB_HOST -u $DB_USER -p$DB_PASS $DB_NAME -e "TRUNCATE TABLE uygulama_log;" 2>> $LOG_FILE
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] İşlem tamamlandı" >> $LOG_FILE
else
    echo "[$(date '+%Y-%m-%d %H:%M:%S')] HATA: Arşivleme başarısız, tablo temizlenmedi" >> $LOG_FILE
    exit 1
fi

Senaryo 2: Test Ortamını Sıfırlama

Geliştirme ve test süreçlerinde sık sık veritabanını başlangıç durumuna döndürmeniz gerekir. Bir dizi tabloyu sıfırlayıp ardından test verilerini yüklemek yaygın bir senaryodur.

-- Foreign key kontrollerini geçici olarak devre dışı bırak
SET FOREIGN_KEY_CHECKS = 0;

-- Tüm test tablolarını temizle
TRUNCATE TABLE siparis_detaylari;
TRUNCATE TABLE siparisler;
TRUNCATE TABLE musteri_adresleri;
TRUNCATE TABLE musteriler;
TRUNCATE TABLE urunler;
TRUNCATE TABLE kategoriler;

-- Foreign key kontrollerini tekrar aç
SET FOREIGN_KEY_CHECKS = 1;

FOREIGN_KEY_CHECKS = 0 ayarı, TRUNCATE işlemi sırasında foreign key kısıtlamalarını devre dışı bırakır. Bu ayarı işlem bittikten sonra mutlaka tekrar aktif etmelisiniz.

Senaryo 3: Staging Ortamından Production Verilerini Kopyalama

Bazen staging ortamına production verilerini çekmek için önce staging tablolarını temizlemeniz gerekir:

-- staging.sh

#!/bin/bash
PROD_DB="production_db"
STAGE_DB="staging_db"
MYSQL_OPTS="-u replication_user -p'gizli_sifre'"

# Staging tablolarını temizle
mysql $MYSQL_OPTS $STAGE_DB << 'EOF'
SET FOREIGN_KEY_CHECKS = 0;
TRUNCATE TABLE urunler;
TRUNCATE TABLE stok;
TRUNCATE TABLE fiyatlar;
SET FOREIGN_KEY_CHECKS = 1;
EOF

# Production'dan veri çek ve staging'e yükle
mysqldump $MYSQL_OPTS --no-create-info $PROD_DB urunler stok fiyatlar | mysql $MYSQL_OPTS $STAGE_DB

echo "Staging güncelleme tamamlandı: $(date)"

Foreign Key Constraint Durumunda TRUNCATE

TRUNCATE komutunun en sık karşılaşılan sorunlarından biri foreign key kısıtlamalarıdır. Parent tabloyu truncate etmeye çalıştığınızda şu hatayı alırsınız:

ERROR 1701 (42000): Cannot truncate a table referenced in a foreign key constraint

Bu durumun üstesinden gelmenin birkaç yolu vardır:

Yöntem 1: FOREIGN_KEY_CHECKS devre dışı bırakmak

SET FOREIGN_KEY_CHECKS = 0;
TRUNCATE TABLE parent_tablo;
TRUNCATE TABLE child_tablo;
SET FOREIGN_KEY_CHECKS = 1;

Yöntem 2: Önce child, sonra parent tabloyu temizlemek

-- Önce bağımlı tabloyu temizle
TRUNCATE TABLE siparis_kalemleri;
-- Sonra parent tabloyu temizle
TRUNCATE TABLE siparisler;

Bu yaklaşım daha güvenlidir çünkü foreign key kontrollerini kapatmaz, sadece doğru sırayı takip eder.

TRUNCATE ile İlgili Önemli Notlar

InnoDB ve MyISAM Davranış Farkları

MariaDB ve MySQL’de en yaygın kullanılan iki storage engine olan InnoDB ve MyISAM, TRUNCATE konusunda bazı farklılıklar gösterir.

InnoDB:

  • TRUNCATE aslında tablo drop edip yeniden oluşturma işlemi yapar
  • Bu nedenle çok büyük tablolarda bile hızlıdır
  • Transaction desteği olan bir engine olmasına rağmen TRUNCATE, implicit commit yapar

MyISAM:

  • Benzer şekilde tablo yeniden oluşturulur
  • AUTO_INCREMENT sıfırlanır

Bu davranışı doğrulamak için şu sorguyu çalıştırabilirsiniz:

-- İşlem öncesi tablo boyutunu kontrol et
SELECT
    table_name,
    table_rows,
    ROUND((data_length + index_length) / 1024 / 1024, 2) AS 'Boyut (MB)'
FROM information_schema.tables
WHERE table_schema = 'veritabani_adi'
AND table_name = 'buyuk_tablo';

-- TRUNCATE uygula
TRUNCATE TABLE buyuk_tablo;

-- İşlem sonrası tablo boyutunu kontrol et
SELECT
    table_name,
    table_rows,
    ROUND((data_length + index_length) / 1024 / 1024, 2) AS 'Boyut (MB)'
FROM information_schema.tables
WHERE table_schema = 'veritabani_adi'
AND table_name = 'buyuk_tablo';

TRUNCATE Transaction İçinde Kullanımı

TRUNCATE bir DDL komutu olduğundan, COMMIT veya ROLLBACK beklemeksizin otomatik olarak commit edilir. Yani şunu yapmaya çalışırsanız:

START TRANSACTION;
TRUNCATE TABLE onemli_tablo;
-- Bir şeyler ters gitti...
ROLLBACK; -- Bu çalışmaz! TRUNCATE geri alınamaz!

Bu nedenle TRUNCATE‘i çok dikkatli kullanmalısınız. Eğer geri alınabilir bir işlem istiyorsanız, büyük tablolarda yavaş olmasına rağmen DELETE kullanmayı tercih etmelisiniz.

Sadece Belirli Bir Partition’ı Temizleme

MySQL 5.6+ ve MariaDB’de partitioned tablolarda belirli bir partition’ı temizleyebilirsiniz:

-- Partitioned tablo örneği
ALTER TABLE satis_verileri
PARTITION BY RANGE (YEAR(satis_tarihi)) (
    PARTITION p2021 VALUES LESS THAN (2022),
    PARTITION p2022 VALUES LESS THAN (2023),
    PARTITION p2023 VALUES LESS THAN (2024),
    PARTITION p_gelecek VALUES LESS THAN MAXVALUE
);

-- Sadece 2021 partition'ını temizle
ALTER TABLE satis_verileri TRUNCATE PARTITION p2021;

Bu yöntem, sadece eski verileri temizlemek istediğinizde tüm tabloyu boşaltmaktan çok daha verimlidir.

Performans Karşılaştırması

Büyük tablolarda TRUNCATE ile DELETE arasındaki performans farkını görmek için şu senaryoyu ele alalım:

-- Önce büyük bir test tablosu oluştur ve doldur
CREATE TABLE performans_test (
    id INT AUTO_INCREMENT PRIMARY KEY,
    veri VARCHAR(255),
    tarih DATETIME DEFAULT NOW()
);

-- 1 milyon satır ekle (bu biraz zaman alacak)
INSERT INTO performans_test (veri)
SELECT MD5(RAND())
FROM information_schema.columns a,
     information_schema.columns b
LIMIT 1000000;

-- DELETE ile silme süresini ölç
SET @baslangic = NOW(6);
DELETE FROM performans_test;
SELECT TIMESTAMPDIFF(MICROSECOND, @baslangic, NOW(6)) / 1000000 AS 'DELETE Suresi (sn)';

-- Tabloyu tekrar doldur
INSERT INTO performans_test (veri)
SELECT MD5(RAND())
FROM information_schema.columns a,
     information_schema.columns b
LIMIT 1000000;

-- TRUNCATE ile silme süresini ölç
SET @baslangic = NOW(6);
TRUNCATE TABLE performans_test;
SELECT TIMESTAMPDIFF(MICROSECOND, @baslangic, NOW(6)) / 1000000 AS 'TRUNCATE Suresi (sn)';

1 milyon satırlık bir tabloda DELETE işlemi onlarca saniye sürerken, TRUNCATE genellikle 1 saniyenin altında tamamlanır. Bu fark, tablo büyüdükçe daha da belirginleşir.

Güvenli TRUNCATE Pratiği

Üretim ortamında TRUNCATE kullanmadan önce uygulamanız gereken bir kontrol listesi:

-- 1. Tablo satır sayısını kontrol et
SELECT COUNT(*) as toplam_satir FROM kritik_tablo;

-- 2. Son güncelleme zamanını kontrol et
SELECT MAX(guncelleme_tarihi) as son_guncelleme FROM kritik_tablo;

-- 3. Backup al (küçük tablolar için)
CREATE TABLE kritik_tablo_yedek_20241201 AS SELECT * FROM kritik_tablo;

-- 4. TRUNCATE işlemini gerçekleştir
TRUNCATE TABLE kritik_tablo;

-- 5. Yedek tabloyu belirli bir süre sakla, sorun yoksa sil
-- DROP TABLE kritik_tablo_yedek_20241201;

Büyük tablolar için backup almak yerine mysqldump kullanın:

#!/bin/bash
# truncate_oncesi_yedek.sh

TABLO="buyuk_log_tablosu"
DB="uygulama_db"
YEDEK_DIR="/backup/manuel"
TARIH=$(date +%Y%m%d_%H%M%S)

# Yedek al
mysqldump -u root -p'sifre' $DB $TABLO | gzip > "$YEDEK_DIR/${DB}_${TABLO}_${TARIH}.sql.gz"

if [ $? -eq 0 ]; then
    echo "Yedek alındı: ${YEDEK_DIR}/${DB}_${TABLO}_${TARIH}.sql.gz"
    echo "TRUNCATE işlemi yapılıyor..."
    mysql -u root -p'sifre' $DB -e "TRUNCATE TABLE $TABLO;"
    echo "Tamamlandı."
else
    echo "HATA: Yedek alınamadı, TRUNCATE iptal edildi!"
    exit 1
fi

TRUNCATE Yetkisi

Bir son dakika detayı olarak yetki konusuna değinmek gerekir. TRUNCATE TABLE komutunu çalıştırabilmek için kullanıcının ilgili tablo üzerinde DROP yetkisine sahip olması gerekir. DELETE yetkisi yeterli değildir.

-- Bir kullanıcıya TRUNCATE için gerekli yetkiyi ver
GRANT DROP ON veritabani.tablo_adi TO 'uygulama_kullanici'@'localhost';

-- Ya da tüm veritabanı için
GRANT DROP ON veritabani.* TO 'uygulama_kullanici'@'localhost';

-- Yetkileri uygula
FLUSH PRIVILEGES;

-- Kullanıcı yetkilerini kontrol et
SHOW GRANTS FOR 'uygulama_kullanici'@'localhost';

Bu detay, özellikle minimum yetki prensibiyle çalışan ortamlarda önemlidir. Uygulama kullanıcısına gereksiz yere DROP yetkisi vermek istemeyebilirsiniz. Bu durumda TRUNCATE yerine DELETE kullanmanız ya da TRUNCATE işlemini ayrı bir stored procedure içinde gerçekleştirip sadece o procedure için yetki vermeniz daha güvenli olacaktır.

Sonuç

TRUNCATE TABLE, doğru senaryoda kullanıldığında son derece güçlü ve verimli bir araçtır. Özellikle büyük tablolarda performans avantajı çok belirgindir ve AUTO_INCREMENT sıfırlama özelliği test ortamlarında büyük kolaylık sağlar. Ancak geri alınamaz olması, trigger’ları tetiklememesi ve foreign key kısıtlamalarıyla olan karmaşık ilişkisi nedeniyle dikkatli kullanılması gereken bir komuttur.

Üretim ortamında TRUNCATE kullanmadan önce mutlaka yedek alın, foreign key bağımlılıklarını gözden geçirin ve işlemin geri alınamaz olduğunu aklınızda bulundurun. Test ve geliştirme ortamlarında ise çekinmeden kullanabilirsiniz. Log temizleme, periyodik veri sıfırlama ve staging ortamı yönetimi gibi senaryolarda TRUNCATE, hem zaman hem de kaynak tasarrufu açısından DELETE’e kıyasla belirgin bir üstünlük sunar.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir