MariaDB ve MySQL’de LENGTH ve CHAR_LENGTH ile Metin Uzunluğu Ölçme

Veritabanlarında metin alanlarıyla çalışırken en sık ihtiyaç duyduğumuz şeylerden biri, bir kolonun ya da ifadenin kaç karakter veya kaç byte uzunluğunda olduğunu öğrenmektir. MySQL ve MariaDB bu iş için bize iki farklı fonksiyon sunar: LENGTH ve CHAR_LENGTH. İlk bakışta aynı işi yaptıkları sanılsa da aralarında kritik bir fark vardır ve bu farkı bilmeden yazdığınız sorgular sizi beklenmedik sonuçlarla karşı karşıya bırakabilir. Bu yazıda her iki fonksiyonu derinlemesine inceleyecek, gerçek dünya senaryolarında nasıl kullanıldıklarını göreceğiz.

LENGTH ve CHAR_LENGTH Nedir?

LENGTH fonksiyonu bir string ifadenin byte cinsinden uzunluğunu döndürür. Yani her karakteri byte olarak sayar. Latin karakterler için bu genellikle 1’dir ama UTF-8 ile encode edilmiş Türkçe karakterler, Arapça, Çince gibi çok-byte’lı karakterler için durum farklıdır.

CHAR_LENGTH fonksiyonu ise bir string ifadenin karakter cinsinden uzunluğunu döndürür. Bir karakter kaç byte yer kaplıyor olursa olsun, CHAR_LENGTH için her biri 1 sayılır.

Bunu somutlaştıralım. “ş” harfi UTF-8 encoding’de 2 byte yer kaplar. “LENGTH(‘ş’)” sorgusu bize 2 döndürürken, “CHAR_LENGTH(‘ş’)” sorgusu 1 döndürür.

CHARACTER_LENGTH fonksiyonu da mevcuttur; bu CHAR_LENGTH‘in tam eşdeğeridir, sadece bir alias’tır. İkisi de aynı sonucu verir.

Encoding ve Collation ile İlişkisi

Bu iki fonksiyon arasındaki farkı gerçekten anlamak için UTF-8 ve karakter setlerini biraz kavramak gerekir. MySQL ve MariaDB’de utf8mb4 karakter seti günümüzde standart olarak kullanılmalıdır. utf8mb4‘te her karakter 1 ile 4 byte arasında yer kaplar.

  • ASCII karakterler (a-z, A-Z, 0-9, temel noktalama): 1 byte
  • Latin ek karakterler (ş, ğ, ü, ö, ç, ı, é, ñ vb.): 2 byte
  • Bazı dil karakterleri (Arapça, Yunanca vb.): 2-3 byte
  • Emoji ve özel semboller (😀, 🚀 vb.): 4 byte

İşte bu yüzden Türkçe metin içeren tablolarla çalışırken hangi fonksiyonu kullandığınız büyük önem taşır.

İlk Adım: Temel Kullanım

Fonksiyonların temel sözdizimine bakalım:

-- Temel kullanım
SELECT LENGTH('Merhaba');
SELECT CHAR_LENGTH('Merhaba');

-- Türkçe karakter içeren metin
SELECT LENGTH('Günaydın');
SELECT CHAR_LENGTH('Günaydın');

-- Farkı net görmek için yan yana
SELECT
    'Günaydın' AS metin,
    LENGTH('Günaydın') AS byte_uzunluk,
    CHAR_LENGTH('Günaydın') AS karakter_uzunluk;

“Günaydın” kelimesi 8 karakterden oluşur. CHAR_LENGTH bize 8 döndürecektir. Ancak “ü” ve “ı” birer Türkçe karakter olduğundan her biri 2 byte yer kaplar. Bu durumda LENGTH bize 10 döndürecektir (6 ASCII karakter x 1 byte + 2 Türkçe karakter x 2 byte = 10 byte).

Tablo Kolonlarıyla Kullanım

Gerçek bir senaryo üzerinden gidelim. Bir e-ticaret veritabanımız olduğunu ve ürün açıklamalarını yönettiğimizi varsayalım.

-- Örnek tablo oluşturma
CREATE TABLE urunler (
    id INT AUTO_INCREMENT PRIMARY KEY,
    urun_adi VARCHAR(255) NOT NULL,
    aciklama TEXT,
    slug VARCHAR(100),
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Test verisi ekleme
INSERT INTO urunler (urun_adi, aciklama, slug) VALUES
('Ahşap Masa', 'Doğal ahşaptan üretilmiş, dayanıklı ve şık masa', 'ahsap-masa'),
('Çelik Sandalye', 'Paslanmaz çelikten yapılmış ergonomik sandalye', 'celik-sandalye'),
('Deri Koltuk', 'Gerçek deri kaplama, konforlu oturma deneyimi', 'deri-koltuk'),
('Plastik Raf', 'Hafif ve pratik depolama çözümü', 'plastik-raf');

-- Uzunlukları sorgulama
SELECT
    urun_adi,
    LENGTH(urun_adi) AS urun_adi_byte,
    CHAR_LENGTH(urun_adi) AS urun_adi_karakter,
    LENGTH(aciklama) AS aciklama_byte,
    CHAR_LENGTH(aciklama) AS aciklama_karakter
FROM urunler;

Bu sorgu çalıştırıldığında Türkçe karakter içeren satırlarda LENGTH ve CHAR_LENGTH değerlerinin farklılaştığını göreceksiniz. Mesela “Çelik Sandalye” için CHAR_LENGTH 14 döndürürken, “Ç” harfi 2 byte olduğundan LENGTH 15 döndürecektir.

Uzunluk Bazlı Filtreleme Senaryoları

Kısa ve Uzun Açıklamaları Bulmak

Bir içerik yönetim sisteminde SEO açısından metinlerin belirli karakter uzunluklarında olması gerekebilir. Gerçek karakter sayısını öğrenmek istiyorsak CHAR_LENGTH kullanmalıyız.

-- Meta description için 160 karakterden uzun açıklamaları bul
SELECT
    id,
    urun_adi,
    CHAR_LENGTH(aciklama) AS karakter_sayisi,
    aciklama
FROM urunler
WHERE CHAR_LENGTH(aciklama) > 160
ORDER BY CHAR_LENGTH(aciklama) DESC;

-- Çok kısa açıklamaları bul (50 karakterden az)
SELECT
    id,
    urun_adi,
    CHAR_LENGTH(aciklama) AS karakter_sayisi
FROM urunler
WHERE CHAR_LENGTH(aciklama) < 50
ORDER BY karakter_sayisi ASC;

Slug Doğrulama

URL slug’larının belirli bir uzunluğu aşıp aşmadığını kontrol edelim. Slug’larda genellikle ASCII karakterler bulunduğundan burada LENGTH ve CHAR_LENGTH aynı sonucu verecektir, ama iyi pratik olarak karakter sayısını ölçüyorsak her zaman CHAR_LENGTH tercih edilmelidir.

-- 50 karakterden uzun slug'ları tespit et
SELECT
    id,
    urun_adi,
    slug,
    CHAR_LENGTH(slug) AS slug_uzunluk
FROM urunler
WHERE CHAR_LENGTH(slug) > 50
ORDER BY slug_uzunluk DESC;

-- Slug olmayan ya da boş slug kayıtları
SELECT
    id,
    urun_adi
FROM urunler
WHERE slug IS NULL OR CHAR_LENGTH(TRIM(slug)) = 0;

Kullanıcı Girişi Doğrulama Senaryosu

Bir üyelik sisteminde parola uzunluk politikasını uygulayan bir kontrol sorgusu yazmak istediğimizi düşünelim. Burada byte değil karakter sayısı önemlidir, çünkü kullanıcılar Türkçe veya diğer dil karakterlerini parola içinde kullanabilir.

-- Kullanıcı tablosu örneği
CREATE TABLE kullanicilar (
    id INT AUTO_INCREMENT PRIMARY KEY,
    kullanici_adi VARCHAR(50) NOT NULL,
    eposta VARCHAR(150) NOT NULL,
    sifre_hash VARCHAR(255) NOT NULL,
    profil_aciklama TEXT,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Kullanıcı adı uzunluklarını analiz et
SELECT
    kullanici_adi,
    CHAR_LENGTH(kullanici_adi) AS kullanici_adi_uzunluk,
    CASE
        WHEN CHAR_LENGTH(kullanici_adi) < 3 THEN 'Çok kısa'
        WHEN CHAR_LENGTH(kullanici_adi) BETWEEN 3 AND 20 THEN 'Uygun'
        WHEN CHAR_LENGTH(kullanici_adi) > 20 THEN 'Çok uzun'
    END AS durum
FROM kullanicilar
ORDER BY CHAR_LENGTH(kullanici_adi) ASC;

Agregasyon ile Birlikte Kullanım

Tablodaki metin sütunları hakkında istatistiksel bilgi toplamak için LENGTH ve CHAR_LENGTH fonksiyonlarını AVG, MAX, MIN, SUM gibi agregasyon fonksiyonlarıyla birleştirmek son derece güçlü sonuçlar verir.

-- Açıklama alanı istatistikleri
SELECT
    COUNT(*) AS toplam_kayit,
    MIN(CHAR_LENGTH(aciklama)) AS en_kisa_aciklama,
    MAX(CHAR_LENGTH(aciklama)) AS en_uzun_aciklama,
    ROUND(AVG(CHAR_LENGTH(aciklama)), 2) AS ortalama_karakter,
    SUM(LENGTH(aciklama)) AS toplam_byte_boyutu,
    SUM(CHAR_LENGTH(aciklama)) AS toplam_karakter_sayisi
FROM urunler
WHERE aciklama IS NOT NULL;

Bu tür bir sorgu, özellikle veritabanı boyutu optimizasyonu yaparken veya içerik kalitesi raporları oluştururken çok işe yarar. Mesela bir blog platformunda ortalama makale uzunluğunu ölçmek, yazarlara geri bildirim vermek için kullanılabilir.

Kategori Bazlı Uzunluk Analizi

-- Kategori bazlı uzunluk analizi (blog_yazilari tablosu varsayımıyla)
SELECT
    kategori,
    COUNT(*) AS yazi_sayisi,
    MIN(CHAR_LENGTH(icerik)) AS en_kisa,
    MAX(CHAR_LENGTH(icerik)) AS en_uzun,
    ROUND(AVG(CHAR_LENGTH(icerik)), 0) AS ortalama_uzunluk
FROM blog_yazilari
GROUP BY kategori
HAVING AVG(CHAR_LENGTH(icerik)) < 500
ORDER BY ortalama_uzunluk ASC;

Gerçek Dünya Senaryosu: SMS Mesaj Sistemi

Bir SMS gönderim sistemi üzerinde çalıştığınızı düşünün. SMS’lerin 160 karakterle sınırlı olduğunu ve bunun ötesindeki mesajların ek ücrete tabi olduğunu biliyorsunuz.

-- SMS mesaj tablosu
CREATE TABLE sms_mesajlar (
    id INT AUTO_INCREMENT PRIMARY KEY,
    alici_numara VARCHAR(15) NOT NULL,
    mesaj_icerigi TEXT NOT NULL,
    gonderim_zamani TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
    durum ENUM('bekliyor', 'gonderildi', 'basarisiz') DEFAULT 'bekliyor'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

-- Mesaj uzunluklarını ve SMS kredi kullanımını hesapla
SELECT
    id,
    alici_numara,
    CHAR_LENGTH(mesaj_icerigi) AS karakter_sayisi,
    CEIL(CHAR_LENGTH(mesaj_icerigi) / 160.0) AS sms_adedi,
    CASE
        WHEN CHAR_LENGTH(mesaj_icerigi) <= 160 THEN 'Tek SMS'
        WHEN CHAR_LENGTH(mesaj_icerigi) <= 320 THEN 'Çift SMS'
        ELSE CONCAT(CEIL(CHAR_LENGTH(mesaj_icerigi) / 160.0), ' SMS')
    END AS sms_bilgisi
FROM sms_mesajlar
ORDER BY karakter_sayisi DESC;

-- Fazla SMS kredi harcayan mesajları bul
SELECT
    COUNT(*) AS coklu_sms_sayisi,
    SUM(CEIL(CHAR_LENGTH(mesaj_icerigi) / 160.0)) AS toplam_sms_kredisi,
    SUM(CEIL(CHAR_LENGTH(mesaj_icerigi) / 160.0)) - COUNT(*) AS ekstra_kredi
FROM sms_mesajlar
WHERE CHAR_LENGTH(mesaj_icerigi) > 160;

Encoding Problemi Tespit Senaryosu

Veri taşıma veya import işlemlerinden sonra encoding problemleri yaşadığınızda LENGTH ve CHAR_LENGTH arasındaki fark size problemin nerede olduğunu gösterebilir. Eğer LENGTH değeri CHAR_LENGTH değerine eşitse ve veride Türkçe karakter bulunduğunu biliyorsanız, o zaman bir encoding problemi var demektir; veriler muhtemelen latin1 olarak kaydedilmiştir.

-- Potansiyel encoding problemi olan kayıtları tespit et
-- Türkçe karakter içerdiği bilinen bir metin sütununda
-- LENGTH == CHAR_LENGTH ise encoding hatası olabilir
SELECT
    id,
    urun_adi,
    LENGTH(urun_adi) AS byte_uzunluk,
    CHAR_LENGTH(urun_adi) AS karakter_uzunluk,
    CASE
        WHEN LENGTH(urun_adi) = CHAR_LENGTH(urun_adi)
             AND urun_adi REGEXP '[ğüşıöçĞÜŞİÖÇ]' = 0
        THEN 'Olası encoding sorunu'
        ELSE 'Normal'
    END AS encoding_durumu
FROM urunler
WHERE LENGTH(urun_adi) = CHAR_LENGTH(urun_adi);

Performans Notları

LENGTH ve CHAR_LENGTH fonksiyonlarını büyük tablolarda WHERE koşulunda kullanırken dikkatli olmak gerekir. Bu fonksiyonlar indeks kullanımını engeller ve full table scan yapılmasına neden olur. Milyonluk tablolarda bu ciddi performans sorunlarına yol açabilir.

-- Kötü pratik: WHERE koşulunda fonksiyon kullanımı (index kullanmaz)
SELECT * FROM urunler WHERE CHAR_LENGTH(urun_adi) > 20;

-- Daha iyi yaklaşım: Hesaplanan uzunluğu ayrı bir kolona sakla
ALTER TABLE urunler ADD COLUMN urun_adi_uzunluk SMALLINT UNSIGNED;

UPDATE urunler SET urun_adi_uzunluk = CHAR_LENGTH(urun_adi);

-- Bu kolona index ekle
CREATE INDEX idx_urun_adi_uzunluk ON urunler(urun_adi_uzunluk);

-- Artık index kullanılır
SELECT * FROM urunler WHERE urun_adi_uzunluk > 20;

Eğer sık sık uzunluğa göre filtreleme yapmanız gerekiyorsa, generated column özelliği de kullanılabilir:

-- MariaDB 5.2+ ve MySQL 5.7+ için generated column
ALTER TABLE urunler
ADD COLUMN urun_adi_char_len SMALLINT UNSIGNED
AS (CHAR_LENGTH(urun_adi)) STORED;

-- Index ekle
CREATE INDEX idx_char_len ON urunler(urun_adi_char_len);

LENGTH ile Binary Veri Kontrolü

LENGTH fonksiyonu özellikle binary veri ve byte boyutu önemli olduğunda tercih edilir. Örneğin bir kolonda saklanan JSON verisinin ya da XML’in depolama boyutunu ölçmek istediğinizde:

-- JSON verisinin byte boyutunu ölç
SELECT
    id,
    LENGTH(json_data) AS byte_boyut,
    CHAR_LENGTH(json_data) AS karakter_sayisi,
    ROUND(LENGTH(json_data) / 1024, 2) AS kb_boyut
FROM yapilandirma_tablosu
WHERE LENGTH(json_data) > 10240  -- 10KB üzeri kayıtlar
ORDER BY LENGTH(json_data) DESC
LIMIT 20;

NULL Değerlerle Davranış

Her iki fonksiyon da NULL değerler için NULL döndürür. Bu bazen beklenmedik sonuçlara yol açabilir.

-- NULL değerlerle test
SELECT
    LENGTH(NULL) AS length_null,          -- NULL döner
    CHAR_LENGTH(NULL) AS char_length_null, -- NULL döner
    LENGTH('') AS length_bos,              -- 0 döner
    CHAR_LENGTH('') AS char_length_bos;    -- 0 döner

-- NULL güvenli sorgu yazmak için COALESCE kullan
SELECT
    id,
    urun_adi,
    COALESCE(CHAR_LENGTH(aciklama), 0) AS aciklama_uzunluk
FROM urunler
ORDER BY aciklama_uzunluk DESC;

Hangi Fonksiyonu Ne Zaman Kullanmalısınız?

CHAR_LENGTH kullanım senaryoları:

  • Kullanıcıya gösterilen metin uzunlukları: Bir tweet, bio veya açıklama alanında kullanıcının kaç karakter girdiğini ölçmek
  • SEO meta etiket kontrolü: Meta description ve title’ın karakter sınırlarını kontrol etmek
  • Form validasyonu: Kullanıcı girdilerinin maksimum uzunluk kurallarına uygunluğunu denetlemek
  • SMS ve mesajlaşma sistemleri: Mesajın kaç karakter içerdiğini ölçmek

LENGTH kullanım senaryoları:

  • Depolama analizi: Bir kolonun veritabanında ne kadar byte yer kapladığını ölçmek
  • Network transfer boyutu: API’dan dönen verinin yaklaşık byte boyutunu hesaplamak
  • Binary içerik kontrolü: BLOB veya BINARY alanlarda boyut denetimi
  • Encoding doğrulama: Beklenen encoding ile gerçek encoding’in karşılaştırılması

Sonuç

LENGTH ve CHAR_LENGTH fonksiyonları birbirinin yerine kullanılabilirmiş gibi görünse de özellikle Türkçe, Arapça, Japonca gibi çok-byte’lı karakterler içeren veritabanlarıyla çalışırken aralarındaki fark kritik hale gelir. Kural olarak şunu benimseyebilirsiniz: kullanıcı deneyimiyle ilgili olan her şeyde CHAR_LENGTH, teknik boyut ve depolama ile ilgili olan her şeyde LENGTH kullanın.

Türkçe metin içeren tablolarda LENGTH kullanarak “karakter sayısı” ölçmeye çalışmak sizi yanlış sonuçlara götürür ve bunun üzerine kurulu iş mantığı hatalı çalışır. Bir SMS sisteminde 160 karakter sınırını LENGTH ile kontrol ederseniz, Türkçe karakter içeren mesajları erken kesmek ya da geç kesmek gibi problemlerle karşılaşırsınız.

Son olarak, büyük tablolarda bu fonksiyonları WHERE koşulunda doğrudan kullanmak yerine hesaplanan değerleri ayrı kolonlarda saklamayı ve bu kolonları indekslemeyi düşünün. Bu yaklaşım sorgu performansınızı dramatik biçimde artıracaktır. Günlük yönetim işlerinde sık kullandığım bir alışkanlık olarak, import sonrası her zaman birkaç satır için LENGTH ve CHAR_LENGTH değerlerini karşılaştırarak encoding sağlığını kontrol ederim; bu basit kontrol birçok baş ağrısını baştan önler.

Bir yanıt yazın

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