MariaDB ve MySQL’de TRIM ile Metin Başı ve Sonu Boşlukları Temizleme

Veritabanı yönetiminde en sık karşılaşılan ama en kolay gözden kaçan sorunlardan biri, metin verilerinin başında veya sonunda gizlenen boşluk karakterleridir. Kullanıcı bir forma veri girerken farkında olmadan eklediği boşluk, veri tabanına öyle kaydolur. Sonra sorgu yazdığında neden sonuç gelmediğini anlayamaz. Siz de saatler geçirip WHERE isim = 'Ahmet' sorgusunun neden çalışmadığını araştırırsınız. Oysa kayıttaki değer ' Ahmet' ya da 'Ahmet 'dir. İşte TRIM fonksiyonu tam bu noktada hayat kurtarır.

TRIM Fonksiyonu Nedir ve Neden Önemlidir

MariaDB ve MySQL’de TRIM() fonksiyonu, bir metin değerinin başındaki, sonundaki ya da her iki tarafındaki belirli karakterleri temizlemek için kullanılır. Varsayılan davranışı boşluk karakterlerini silmektir ancak bunu özelleştirmek de mümkündür.

Sysadmin olarak günlük işlerinizde bu fonksiyonla karşılaşacağınız durumlar şunlardır:

  • Kullanıcı giriş formlarından gelen kirli verileri temizleme
  • CSV veya Excel’den import edilen verilerdeki fazla boşlukları düzeltme
  • Farklı sistemlerden senkronize edilen tablolardaki tutarsızlıkları giderme
  • ETL süreçlerinde veri standardizasyonu
  • Raporlama sorgularında yanlış eşleşmeleri önleme

Uygulama katmanında bu temizlik yapılmasa bile veritabanı seviyesinde müdahale edebilirsiniz. Bu esneklik, özellikle üretim sistemlerinde kritik önem taşır.

Temel Kullanım Sözdizimi

TRIM fonksiyonunun üç temel kullanım biçimi vardır:

-- Sadece LTRIM: Sol taraftaki (baştaki) boşlukları siler
SELECT LTRIM('  Merhaba Dünya  ');
-- Sonuç: 'Merhaba Dünya  '

-- Sadece RTRIM: Sağ taraftaki (sondaki) boşlukları siler
SELECT RTRIM('  Merhaba Dünya  ');
-- Sonuç: '  Merhaba Dünya'

-- TRIM: Her iki taraftaki boşlukları siler
SELECT TRIM('  Merhaba Dünya  ');
-- Sonuç: 'Merhaba Dünya'

Görüldüğü gibi LTRIM ve RTRIM tek taraflı çalışırken TRIM her iki ucu da temizler. Çoğu senaryoda TRIM kullanmak yeterli olacaktır.

TRIM Fonksiyonunun Gelişmiş Sözdizimi

TRIM fonksiyonu sadece boşlukla sınırlı değildir. İstediğiniz karakteri belirtebilirsiniz:

-- LEADING: Sadece baştaki belirli karakteri siler
SELECT TRIM(LEADING '/' FROM '/var/log/nginx/');
-- Sonuç: 'var/log/nginx/'

-- TRAILING: Sadece sondaki belirli karakteri siler
SELECT TRIM(TRAILING '/' FROM '/var/log/nginx/');
-- Sonuç: '/var/log/nginx'

-- BOTH: Her iki taraftaki belirli karakteri siler
SELECT TRIM(BOTH '#' FROM '###başlık###');
-- Sonuç: 'başlık'

Bu özellik özellikle log verisi işlerken ya da dosya yollarını normalize ederken çok işe yarar.

Gerçek Dünya Senaryosu 1: Kullanıcı Tablosundaki Kirli Veriler

Diyelim ki şirketin web uygulaması geçen ay yeni bir kayıt formu devreye aldı ve o tarihten bu yana gelen kullanıcı adlarının bir kısmı boşlukla kaydediliyor. Destek ekibi size “şu kullanıcı neden sisteme giremiyor” diye geliyor. Sorunu tespit edelim:

-- Önce kirli kayıtları tespit edelim
SELECT id, kullanici_adi, LENGTH(kullanici_adi) AS uzunluk,
       LENGTH(TRIM(kullanici_adi)) AS temiz_uzunluk
FROM kullanicilar
WHERE LENGTH(kullanici_adi) != LENGTH(TRIM(kullanici_adi));

Bu sorgu, TRIM öncesi ve sonrası karakter sayısı farklı olan yani başında veya sonunda boşluk olan tüm kayıtları listeler. Kaç kayıt etkilendiğini görünce şaşırabilirsiniz.

Şimdi bu kayıtları düzeltelim:

-- Güvenli güncelleme: Önce test et
SELECT id, kullanici_adi, TRIM(kullanici_adi) AS temizlenmis
FROM kullanicilar
WHERE LENGTH(kullanici_adi) != LENGTH(TRIM(kullanici_adi))
LIMIT 10;

-- Her şey doğruysa güncelle
UPDATE kullanicilar
SET kullanici_adi = TRIM(kullanici_adi)
WHERE LENGTH(kullanici_adi) != LENGTH(TRIM(kullanici_adi));

Önemli not: Üretim sisteminde herhangi bir UPDATE çalıştırmadan önce mutlaka yedek alın. mysqldump ile etkilenecek tabloyu yedekleyin ya da en azından bir transaction içinde çalışın.

Gerçek Dünya Senaryosu 2: CSV Import Sonrası Veri Temizleme

Muhasebe departmanı size bir Excel dosyası gönderdi. Siz bunu CSV olarak dışa aktarıp MySQL’e import ettiniz. Ama sorgular beklendiği gibi çalışmıyor. Problem nerede?

Excel’den gelen veriler çoğu zaman hücrelerin başında ve sonunda gizli boşluklar içerir. Özellikle formülle oluşturulmuş hücreler bu sorunu sık yaratır.

-- Import edilen ürün tablosunu kontrol edelim
SELECT urun_kodu, urun_adi,
       CONCAT('[', urun_kodu, ']') AS ham_kod,
       CONCAT('[', TRIM(urun_kodu), ']') AS temiz_kod
FROM urunler
ORDER BY urun_kodu
LIMIT 20;

Köşeli parantezler arasında göreceğiniz [ AHMET001] gibi çıktılar, başında boşluk olduğunu açıkça ortaya koyar.

Bir adım daha ileri giderek tüm metin kolonlarını aynı anda temizleyebilirsiniz:

-- Birden fazla kolonu aynı anda temizleme
UPDATE urunler
SET urun_kodu = TRIM(urun_kodu),
    urun_adi = TRIM(urun_adi),
    kategori = TRIM(kategori),
    tedarikci_kodu = TRIM(tedarikci_kodu)
WHERE urun_kodu != TRIM(urun_kodu)
   OR urun_adi != TRIM(urun_adi)
   OR kategori != TRIM(kategori)
   OR tedarikci_kodu != TRIM(tedarikci_kodu);

TRIM ile JOIN ve WHERE Kullanımı

Sadece veri temizliği değil, sorgulama sırasında da TRIM’den yararlanabilirsiniz. Özellikle verinin temizlenmesi için zamanınız yoksa ya da kaynak sistemde değişiklik yapamıyorsanız, sorgu anında TRIM uygulamak geçici bir çözüm sunar.

-- Kirli veri olsa bile doğru eşleşme sağlayan JOIN
SELECT m.musteri_adi, s.siparis_no, s.tutar
FROM musteriler m
INNER JOIN siparisler s ON TRIM(m.musteri_kodu) = TRIM(s.musteri_kodu)
WHERE TRIM(m.musteri_adi) LIKE '%Teknoloji%';

Ancak burada dikkat etmeniz gereken kritik bir nokta var: TRIM fonksiyonu indeks kullanımını engeller. Eğer musteri_kodu kolonu üzerinde bir indeks varsa, TRIM(musteri_kodu) şeklinde fonksiyon uygulandığında bu indeks kullanılamaz. Büyük tablolarda performans sorunu yaratabilir. Bu nedenle mümkünse veriyi önceden temizleyip indeksi aktif tutmak daha sağlıklıdır.

Bunu EXPLAIN ile doğrulayabilirsiniz:

-- İndeks kullanımını kontrol et
EXPLAIN SELECT * FROM musteriler WHERE musteri_kodu = 'MST001';

-- TRIM ile indeks kullanımı (dikkat!)
EXPLAIN SELECT * FROM musteriler WHERE TRIM(musteri_kodu) = 'MST001';

İki sorgunun type sütununu karşılaştırın. İlkinde ref ya da eq_ref görürken ikincisinde ALL görüyorsanız full table scan yapılıyor demektir.

View ile Kalıcı Temizleme Katmanı Oluşturma

Uygulamanın kaynak kodunu değiştiremiyorsunuz ama veri her sorguda kirli geliyor. Bu durumda bir view oluşturarak sorgu anında temizleme uygulayabilirsiniz:

-- Temiz veri sunan bir view oluştur
CREATE OR REPLACE VIEW temiz_musteriler AS
SELECT
    id,
    TRIM(musteri_adi) AS musteri_adi,
    TRIM(e_posta) AS e_posta,
    TRIM(telefon) AS telefon,
    TRIM(adres) AS adres,
    TRIM(sehir) AS sehir,
    kayit_tarihi
FROM musteriler;

Artık uygulama musteriler tablosu yerine temiz_musteriler view’ını kullanabilir. Veri temizleme mantığı tek bir yerde merkezi olarak yönetilir.

TRIM ile Birlikte Sık Kullanılan Diğer Fonksiyonlar

Pratikte TRIM genellikle yalnız çalışmaz. Diğer string fonksiyonlarıyla birleştirilince asıl gücü ortaya çıkar:

-- TRIM + UPPER + REPLACE kombinasyonu
-- Telefon numaralarını standardize etme
SELECT
    telefon AS ham_telefon,
    TRIM(REPLACE(REPLACE(REPLACE(telefon, '-', ''), ' ', ''), '(', '')) AS temiz_telefon
FROM kisiler
WHERE telefon IS NOT NULL;

-- TRIM + LOWER ile e-posta normalleştirme
UPDATE uyeler
SET e_posta = TRIM(LOWER(e_posta))
WHERE e_posta IS NOT NULL;

-- TRIM + CONCAT ile temiz rapor çıktısı
SELECT
    CONCAT(TRIM(ad), ' ', TRIM(soyad)) AS tam_ad,
    TRIM(departman) AS departman
FROM personel
ORDER BY soyad, ad;

Stored Procedure ile Otomatik Temizleme

Büyük sistemlerde veri temizleme işini manuel yapmak yerine bir stored procedure yazıp bunu düzenli olarak çalıştırabilirsiniz. Hatta bir event scheduler ile bunu otomatikleştirebilirsiniz.

-- Tablo temizleme stored procedure'ü
DELIMITER //

CREATE PROCEDURE veri_temizle()
BEGIN
    DECLARE etkilenen_satir INT DEFAULT 0;

    -- Kullanıcı adlarını temizle
    UPDATE kullanicilar
    SET kullanici_adi = TRIM(kullanici_adi),
        e_posta = TRIM(LOWER(e_posta))
    WHERE kullanici_adi != TRIM(kullanici_adi)
       OR e_posta != TRIM(LOWER(e_posta));

    SET etkilenen_satir = ROW_COUNT();

    -- Log tablosuna yaz
    INSERT INTO temizlik_log (tablo_adi, etkilenen_kayit, islem_tarihi)
    VALUES ('kullanicilar', etkilenen_satir, NOW());

    SELECT CONCAT(etkilenen_satir, ' kayıt temizlendi.') AS sonuc;
END //

DELIMITER ;

-- Procedure'ü çalıştır
CALL veri_temizle();

Bu yaklaşım sayesinde her gece belirli bir saatte çalıştırmak üzere MariaDB Event Scheduler ile zamanlayabilirsiniz.

NULL Değerlerle Başa Çıkma

TRIM ile çalışırken NULL değerlere dikkat etmek gerekir. MySQL ve MariaDB’de NULL üzerinde herhangi bir fonksiyon çalıştırdığınızda sonuç yine NULL döner. Bu davranış genellikle beklenen şeydir ama bazen sizi şaşırtabilir:

-- NULL davranışını inceleyelim
SELECT
    TRIM(NULL),           -- NULL döner
    TRIM(''),             -- Boş string döner
    TRIM('   '),          -- Boş string döner
    LENGTH(TRIM('   ')); -- 0 döner

-- NULL ve boş string ayrımını yapmak
SELECT
    id,
    kullanici_adi,
    CASE
        WHEN kullanici_adi IS NULL THEN 'NULL kayıt'
        WHEN TRIM(kullanici_adi) = '' THEN 'Boş kayıt'
        ELSE TRIM(kullanici_adi)
    END AS temiz_deger
FROM kullanicilar;

-- COALESCE ile NULL güvenli TRIM
SELECT TRIM(COALESCE(kullanici_adi, '')) AS kullanici_adi
FROM kullanicilar;

NULL ve boş string aynı şey değildir. TRIM(' ') size boş string döndürür, NULL döndürmez. Bunu aklınızda tutun.

Performans Optimizasyonu: Generated Column Yöntemi

Önceki bölümlerde TRIM fonksiyonunun indeks kullanımını engellediğinden bahsetmiştik. Bunu aşmanın akıllıca bir yolu, MySQL 5.7 ve MariaDB 5.2 ile gelen generated column özelliğini kullanmaktır:

-- Temizlenmiş değeri tutan virtual kolon ekle
ALTER TABLE musteriler
ADD COLUMN musteri_kodu_temiz VARCHAR(50)
    GENERATED ALWAYS AS (TRIM(musteri_kodu)) VIRTUAL;

-- Bu kolon üzerine indeks oluştur
CREATE INDEX idx_musteri_kodu_temiz ON musteriler(musteri_kodu_temiz);

-- Artık indeks kullanarak sorgulayabilirsiniz
SELECT * FROM musteriler WHERE musteri_kodu_temiz = 'MST001';

Bu yöntemde musteri_kodu_temiz kolonu fiziksel olarak depolanmaz (VIRTUAL), her sorgu anında hesaplanır. Ama indeks kullanılabildiği için performans korunur.

Eğer çok sık sorguluyorsanız ve hesaplama maliyetini azaltmak istiyorsanız STORED olarak da oluşturabilirsiniz:

-- STORED version: Diskde tutulur, daha hızlı okuma
ALTER TABLE musteriler
ADD COLUMN musteri_kodu_temiz VARCHAR(50)
    GENERATED ALWAYS AS (TRIM(musteri_kodu)) STORED;

Yaygın Hatalar ve Çözümleri

Sysadmin olarak bu fonksiyonla çalışırken karşılaştığım tipik hataları ve çözümlerini paylaşayım:

Problem: TRIM yaptım ama eşleşme hala çalışmıyor. Çözüm: Bazen sorun boşluk değil, tab karakteri ya da non-breaking space (Unicode 0xA0) gibi görünmez karakterlerdir. Bunları TRIM temizleyemez. ORD() ve HEX() fonksiyonlarıyla karakteri tespit edin:

-- Görünmez karakterleri tespit et
SELECT HEX(musteri_adi), LENGTH(musteri_adi)
FROM musteriler
WHERE id = 42;

-- Tab, satır sonu gibi karakterleri de temizle
UPDATE musteriler
SET musteri_adi = TRIM(REPLACE(REPLACE(REPLACE(musteri_adi,
    CHAR(9), ''),   -- Tab
    CHAR(10), ''),  -- Satır sonu (LF)
    CHAR(13), ''))  -- Satır başı (CR)
WHERE id = 42;

Problem: Büyük tabloda UPDATE çok uzun süruyor. Çözüm: Parçalı güncelleme yapın:

-- Büyük tablolarda batch update
UPDATE kullanicilar
SET kullanici_adi = TRIM(kullanici_adi)
WHERE id BETWEEN 1 AND 10000
  AND kullanici_adi != TRIM(kullanici_adi);

-- Sonra 10001-20000, 20001-30000 şeklinde devam edin
-- Ya da bunu bir script ile otomatize edin

Sonuç

TRIM ve kardeş fonksiyonları LTRIM, RTRIM basit görünse de doğru kullanıldığında veri kalitesi sorunlarının büyük bir kısmını çözer. Özellikle dışarıdan veri alınan sistemlerde, legacy uygulamalardan gelen kayıtlarda ve çoklu kaynak entegrasyonlarında bu fonksiyonlar günlük iş akışınızın bir parçası haline gelir.

Şu pratik kuralları aklınızda tutun:

  • Veriyi kaynakta temizlemek her zaman en iyisidir. Uygulama katmanına müdahale edebiliyorsanız oradan başlayın.
  • WHERE ve JOIN içinde TRIM kullanmak zorundaysanız bunun indeks performansına etkisini EXPLAIN ile ölçün.
  • Büyük tablolarda toplu UPDATE öncesi mutlaka yedek alın ve önce küçük bir örnekle test edin.
  • Generated column ve indeks kombinasyonu, hem temiz veri hem de iyi performans istediğinizde güçlü bir çözüm sunar.
  • TRIM’in NULL ile davranışını anlayın; NULL kayıtlar için COALESCE veya CASE kullanmayı alışkanlık haline getirin.
  • Tekrarlayan temizlik işleri için stored procedure yazın ve event scheduler ile otomatize edin.

Veri kalitesi sadece uygulama geliştiricilerin sorumluluğu değildir. Sistem yöneticisi olarak veritabanı seviyesinde bu tür önlemleri almak, uzun vadede hem destek yükünü hem de hata ayıklama süresini ciddi ölçüde azaltır.

Bir yanıt yazın

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