Geliştirici Araçları

T-SQL’de Regex Artık LOB’u da Yutuyor: 2 MB Devri

Açık konuşayım: SQL Server tarafında regex işi yıllarca beni en çok yoran konulardan biriydi. Yıllar boyunca ya CLR’a sarıldık, ya da veriyi uygulama katmanına çekip orada işledik. Hatta bazen — itiraf edeyim — biraz utanç verici şekilde LIKE '%...%' zincirleri kurup işi yürüttüğümüz de oldu. Şimdi güzel kısma gelelim: SQL Server 2025 CU5 ile birlikte varchar(max) ve nvarchar(max) girdiler, yedi regex fonksiyonunun hepsinde 2 MB’a kadar destekleniyor. Siz ne dersiniz? Yani o 8000 byte sınırına takılıp log dosyalarını parça parça bölme derdi, en azından bu senaryoda, baya azalıyor.

Kendi deneyimimden konuşuyorum, Ben bu özelliği geçen hafta bir müşterimde test etme fırsatı buldum — büyük bir e-ticaret firmasının webhook log tablosunda. Sonucu söyleyeyim: şaşırdım açıkçası. Ama dür bir saniye, önce neden buna (söylemesi ayıp) ihtiyaç duyduğumuzu netleştireyim. Mevzu sadece “regex geldi” meselesi değil, gerçek hayatta veri boyutu büyüyünce eski alışkanlıklar biraz komik kalıyor.

Hikâyenin Başı: T-SQL Regex Neden Geç Geldi?

SQL Server’da native regex desteğinin yıllarca olmaması, açık konuşayım, hepimizin canını sıkıyordu (en azından benim deneyimim böyle). PostgreSQL tarafında ~ operatörü var, Oracle’da REGEXP_LIKE çoktan yerleşmişti, MySQL de bir şekilde işi çeviriyordu; Microsoft cephesinde işe biz uzun süre SQLCLR ile RegexMatch benzeri sarmalayıcılar yazıp durduk. Hatırlıyorum, 2017’de bir bankacılık projesinde tam 3 gün boyunca CLR assembly’lerinin UNSAFE PERMISSION SET yüzünden production’a açılmasıyla boğuşmuştum, güvenlik ekibinden onay almak için epey ter dökmüştük.

Bir dakika — bununla bitmedi.

Evet.

Açık konuşayım, Ignite 2025’te regex’in GA olduğunu duyunca hemen kurcaladım. Güzel haberdi, ama ilk sürümde LOB desteği yoktu; yani varchar(max) tipindeki bir sütunu doğrudan REGEXP_REPLACE‘e veremiyordunuz, önce varchar(8000)‘e cast etmek ya da veriyi bölmek gerekiyordu. CU5 işte o boşluğu kapatıyor, küçük gibi duruyor ama pratikte baya iş görüyor.

RE2 Motoru: Neden Bu Kadar Önemli?

İşin teknik tarafına biraz dalalım. Neden önemli bu? T-SQL regex motoru POSIX ERE (Extended Regular Expression) semantiğini uyguluyor ve altında Google’ın RE2 kütüphanesi çalışıyor; buradaki kritik nokta şu ki RE2 doğrusal zamanlı gidiyor ve geri izleme yapmıyor, yani klasik motorların bazen içine düştüğü o garip kuyudan uzak duruyor.

Bak şimdi, İşte, peki neden? Çünkü PCRE ya da.NET Regex gibi geleneksel motorlarda meşhur bir bela var: catastrophic backtracking. Kötü yazılmış bir regex, mesela (a+)+b, birkaç yüz karakterlik bir input’ta bile motoru kilitleyebiliyor; buna ReDoS (Regular Expression Denial of Service) deniyor ve lafı gevelemeden söyleyeyim, bu gerçek bir güvenlik açığı (ciddiyim). 2019’da Cloudflare’in 27 dakikalık global kesintisi de tam olarak bu sınıfa giren bir problem yüzünden olmuştu (ben de ilk duyduğumda şaşırmıştım)

İşte tam da bu noktada devreye giriyor.

RE2 motoru ReDoS’a karşı yapısal olarak bağışık. 1.8 MB’lık bir log blob’unda regex çalıştırırken bu garanti çok kıymetli — çünkü 8000 byte’lık bir string için tolere edebileceğiniz risk, 2 MB’lık veri geldiğinde bir anda başka bir şeye dönüşüyor, hani insanın içi rahat etmiyor.

CU5 ile Tam Olarak Ne Değişti?

İtiraf edeyim, Şöyle toparlayayım, çünkü release notes tarafı biraz dağınık kalmış; ilk okuduğumda ben de “tam olarak neye dokunmuşlar?” diye durup baktım, sonra tabloyu yan yana koyunca iş netleşti.

Fonksiyon GA (Kasım 2025) CU5 sonrası
REGEXP_LIKE LOB ✓ LOB ✓ (2 MB)
REGEXP_COUNT LOB ✓ LOB ✓ (2 MB)
REGEXP_INSTR LOB ✓ LOB ✓ (2 MB)
REGEXP_REPLACE LOB ✗ LOB ✓ (2 MB)
REGEXP_SUBSTR LOB ✗ LOB ✓ (2 MB)
REGEXP_MATCHES (TVF) LOB ✗ LOB ✓ (2 MB)
REGEXP_SPLIT_TO_TABLE (TVF) LOB ✗ LOB ✓ (2 MB)

Eh, Kısacası tablo şu: bazı fonksiyonlar zaten LOB okuyabiliyordu, CU5 ile eksik kalanlar da geldi, ama bir nokta var ki atlanırsa sonra insanın başı ağrıyor; pattern kısmı, yani regex deseninin kendisi, hâlâ 8000 byte sınırında kalıyor.

Çok konuştum, örnekle göstereyim.

Küçük bir detay: Evet, aynen öyle.

Açık konuşayım, 8000 byte uzunluğunda regex yazmaya kalkıyorsanız orada başka bir sorun vardır; bu iş biraz fazla kaçmış demektir, çünkü bakım tarafı çabucak içinden çıkılmaz hâle geliyor. Sonradan kimse “bunu neden böyle yapmışız?” diye sormadan edemiyor.

💡 Bilgi: 2 MB sınırı tek bir fonksiyon çağrısına geçirilen input için geçerli. Yani varchar(max) sütununuzda 2 GB’lık bir değer durabilir; sadece regex değerlendirmesinde o değerin 2 MB’tan büyük kısmını tek seferde işleyemezsiniz. Bu önemli bir nüans.

Peki neden?

Nedeni basit: veri tabanda duruyor diye her yerde aynı rahatlıkla işlenebilecek sanılıyor, ama burada işlem anındaki giriş boyutu sınıra takılıyor; yani depolama ile fonksiyonun tek seferde yuttuğu veri aynı şey değil.

Daha açık söyleyeyim, küçük bir detay: Bir bakıma, tam da öyle.

Sahadan Bir Örnek: Webhook Log Parsing

Geçen hafta bahsettiğim e-ticaret müşterisinde yine benzer bir işe girdik. Tablonun adı WebhookLogs, Payload sütunu nvarchar(max), içeride de ortalama 400 KB ile 1.2 MB arası JSON payload’lar dönüyor. Epey dolu yani. Ödeme sağlayıcılarından gelen webhook’ları saklıyorlar, biz de her payload içinden başarısız işlemlerin transaction_id‘lerini. Hata kodlarını ayıklamaya çalışıyoruz (inanın bana)

Kısacası, küçük bir detay: Eski tarafta ne yapıyorduk? Önce OPENJSON ile parse ediyorduk, sonra path-based extract, ardından filtreleme geliyordu. Çalışıyor mu? Çalışıyor. Ama işin aslı, JSON şeması sağlayıcıdan sağlayıcıya değişince beş ayrı parser yazmak gerekiyordu; hani kağıt üstünde düzenli duruyor ama pratikte insanı yoruyor. Regex ile tek desende toparlamak işe baya iş görüyor: (inanın bana) Daha fazla bilgi için MCP Sunucuları İçin Yönetişim: .NET’e Yeni Toolkit Geldi yazımıza bakabilirsiniz.

Ve işler burada ilginçleşiyor.

SELECT 
l.LogId,
m.match_value AS TransactionId,
m.start_position
FROM WebhookLogs l
CROSS APPLY REGEXP_MATCHES(
l.Payload,
N'"(?:txn|transaction)_id"\s*:\s*"([A-Z0-9\-]{10,40})"',
'i'
) m
WHERE REGEXP_LIKE(l.Payload, N'"status"\s*:\s*"(failed|declined|error)"', 'i')
AND l.CreatedAt >= DATEADD(HOUR, -24, SYSUTCDATETIME());

Şöyle ki, Bu sorgu eskiden CLR tarafında yaklaşık 4-5 saniye sürüyordu, üstelik bu ölçüm yaklaşık 80 bin satır üzerinde alınmıştı. CU5 sonrası native regex ile süre 900 ms civarına indi (ciddiyim). Fena değil. Siz hiç denediniz mi? Yaklaşık 5x hızlanma var ama hemen “tamamdır” demeyeyim, çünkü küçük bir detay daha çıktı karşıma — bence çok yerinde bir karar —

Evet.

Beklediğim Kadar İyi Olmayan Tarafı

Açık konuşayım, paralel plan oluşturma konusunda optimizer’ın regex fonksiyonlarına yaklaşımı hâlâ biraz çekingen duruyor. Büyük tablolarda OPTION (USE HINT('ENABLE_PARALLEL_PLAN_PREFERENCE')) eklemeden serial bir düşüneyim… plan üretebiliyor; sebep de CPU costunu biraz fazla agresif tahmin etmesi gibi görünüyor. Bence burada Microsoft’a feedback vermek lazım, çünkü şu hâliyle bazen gereksiz yere tek çekirdeğe yaslanıyor. Insan “neden böyle yaptı şimdi?” diye kalıyor.

İtiraf edeyim, Neyse uzatmayalım, ilk denememde garip bir hata da aldım: Msg 9100, Level 15 tarzı bir şeydi ve “Possibly corrupt index” diyordu. Halbuki index’le alakası yoktu bile. Sebep şuymuş: paterni nvarchar(max) bir değişkende tutuyordum ama pattern parametresi sadece nvarchar(4000)‘i kabul ediyor; mesaj da bunu düzgün anlatmak yerine saçma sapan şekilde “corrupt index” diye patlıyor. Çözüm basit: paterni açıkça nvarchar(4000)‘e cast etmek gerekiyor.

Tam da öyle.

Türkiye’deki Kurumlar İçin Pratik Değerlendirme

Bak şimdi, Türkiye’deki kurumsal müşterilerde bu özelliğin etkisi en çok üç yerde hissediliyor: bankacılık (loglar. KVKK uyumluluğu için PII maskeleme), e-ticaret (webhook ve API log analizi) ve telekom (CDR — Call Detail Record parsing). Kısa söyledim, ama işin içinde baya iş var.

KVKK tarafı ayrı bir başlık zaten. Bir telco müşterimde 6 ay önce şöyle bir iş yaptık: arşivlenen sözleşme PDF’lerinden çıkarılan metinlerde TC kimlik numarası, telefon, IBAN gibi alanları maskelemek; şu an Python tarafında dönüyordu,. CU5 ile bunu doğrudan SQL’de yapabiliyorsunuz, yani veri dışarı çıkmadan iş bitiyor.

-- TC kimlik no maskeleme (11 haneli, ilk hanesi 0 olmayan)
UPDATE ContractTexts
SET MaskedBody = REGEXP_REPLACE(
OriginalBody,
N'\b[1-9][0-9]{10}\b',
N'*'
)
WHERE LEN(OriginalBody) > 0;

Veri uygulama katmanına gitmeden, network trafiği yaratmadan, SQL Server’ın içinde halloluyor. İyi tarafı bu. Daha açık söyleyeyim, ama dür bir saniye — asıl kazanç sadece performans değil; güvenlik tarafında da eliniz rahatlıyor, (ki bu çoğu kişinin gözünden kaçıyor). Ham veri ortalıkta daha az dolaşıyor. Daha fazla bilgi için C# 16 Unsafe Yeniden Doğuyor: Bellek Güvenliğinde Yeni Çağ yazımıza bakabilirsiniz.

Bir dakika — bununla bitmedi.

Maliyet Tarafı: Azure SQL’de TL Hesabı

Azure SQL Database tarafında bu özellik için ek bir lisans ücreti yok; regex motoru servisin parçası olarak geliyor. Şey gibi düşünün: ayrı bir ürün satın almıyorsunuz, ama dolaylı tarafta nefes alıyorsunuz çünkü veriyi uygulama katmanına çekip orada işlemeye çalıştığınız senaryoda DTU/vCore tüketimi network I/O’ya, app server’a. Transfer maliyetlerine dağılıyordu.

Kabaca bir hesap yaparsak, General Purpose 8 vCore bir Azure SQL Database aylık yaklaşık 25-30 bin TL bandında geziyor. Eğer log parsing işlerinizi SQL’e taşıyıp 2-3 vCore’lük bir app servisini kapatabilirseniz, aylık 8-10 bin TL civarında tasarruf çıkabiliyor; tabi bu herkes için aynı olmaz, senaryoya göre değişir ama yön bence net.

Bunu biraz açayım.

Enterprise vs Startup: Kim Ne Yapmalı?

Şimdi işin pratik tarafına gelelim. Çünkü her şirketin derdi ayrı, startup başka konuşuyor, banka bambaşka yaşıyor.

  • Küçük ekipseniz / startup’sanız: Direkt geçin. Çok da uzatmayın. Azure SQL Database “Always-up-to-date” policy ile zaten otomatik geliyor. CLR’larınız varsa kaldırın, sadeleştirin. Daha az kod, genelde daha az bug demek oluyor; tabii bazen o kadar da temiz gitmiyor ama çoğu senaryoda iş görüyor. (bu kritik)
  • Orta ölçekli kurumsal yapıdaysanız: Önce dev/test ortamında 2-4 hafta bakın. En çok da batch ETL işlerinizdeki regex kullanımlarını profile edin, plan cache davranışını izleyin, bir de beklemediğiniz yerde yavaşlayan sorgu var mı diye gözünüzü açık tutun.
  • Büyük enterprise / banka / kamu iseniz: Acele etmeyin. SQL Server 2025 CU5’i önce yedek bir ortama kurun. 3-6 aylık bir validation süreci geçirin. Mevcut SQLCLR çözümlerinizi paralel olarak çalıştırıp sonuçları karşılaştırın. CLR’ı kaldırmadan önce en az 2 ay regex-only mod gözlemleyin; çünkü işte asıl sürprizler orada çıkıyor.

Ha bu arada, regex pattern’lerinizi de elden geçirin. RE2, PCRE’den biraz farklı davranıyor, yani aynı ifade her yerde aynı sonucu vermiyor; lookahead. Lookbehind de RE2’de desteklenmiyor (en azından şu an), o yüzden.NET ya da Python’dan kopyalayıp yapıştırdığınız bir regex varsa mutlaka test edin. PowerShell macOS’ta Notarize Edildi: Gatekeeper Çilesi Bitti yazımızda bu konuya da değinmiştik.

İşin garibi, Peki neden? Çünkü küçük bir fark bazen bütün akışı bozuyor.

Evet.

İlk Adım Olarak Ne Yapın?

Hemen bugün denemek istiyorsanız, işin başı üç adımda toparlanıyor.

  1. Bir audit yapın: Hangi tablolarınızda varchar(max) / nvarchar(max) sütunlar var, bir bakın; sonra da bunlarda ne tür pattern matching dönüyor, önü çıkarın. sys.dm_exec_query_stats ile LIKE içeren ağır sorguları bulun, çünkü bazen asıl yük tam orada saklanıyor.
  2. Bir POC yazın: En çok kullanılan 3-5 pattern matching senaryosunu seçin, eski yöntemle de deneyin regex ile de; sonra execution time ve IO stats tarafını yan yana koyun. Kâğıt üstünde iyi görünen şey, prod’da bazen tökezliyor, o yüzden ölçmeden karar vermeyin.
  3. Production’a aşamalı geçin: Önce read-only raporlama sorgularında deneyin, bakalım davranış nasıl. Yazma operasyonlarına en son geçin; acele edince küçük bir detay bile can sıkabiliyor. — bunu es geçmeyin

Evet, ben bunu biraz daha geniş bir resmin parçası gibi görüyorum. SQL Server tarafındaki bu native regex hamlesi, Microsoft’un genel veritabanı stratejisinde yön değiştirmenin bir ayağı gibi duruyor; yakın zamanda Cosmos Conf 2026’da veritabanı dünyasının nereye gittiğine dair yazdığım yazıda da değinmiştim — AI çağında veritabanları giderek “veriyi dışarı vermeden işleme” tarafına kayıyor, yani veri içeride kalıyor ama iş görme biçimi değişiyor. Regex de bu hikâyenin içinde duruyor. Benzer şekilde MAESTRO ile SQL tarafında agentic AI güvenliği de aynı felsefenin başka bir yüzü; biri sorgu katmanını değiştiriyor, öteki güvenlik ve kontrol tarafını sıkılaştırıyor.

Dikkat Edilmesi Gereken Tuzaklar

Dürüst olmak gerekirse, Yazıyı kapatmadan önce, sahada başıma gelen üç tuzağı da bırakayım. Kısa kısa geçeceğim, çünkü uzatırsam konu dağılıyor; ama işin aslı bu detaylar çoğu zaman en çok can sıkan yer oluyor.

İtiraf edeyim, Birinçisi: REGEXP_REPLACE ile büyük metinlerde UPDATE atarken transaction log bir anda kabarıyor. 1.5 GB’lık bir tabloda toplu maskeleme yaptığımda log dosyası 8 GB’a fırladı, yani ilk bakışta “bu kadar da olmaz” dedirtiyor; çözüm tarafında işe işi batch’lere bölmek ve her batch sonrasında CHECKPOINT çekmek baya iş görüyor.

Şunu söyleyeyim, Evet.

İkincisi: Collation meselesi. Türkçe karakterlerle çalışıyorsanız, ki çoğumuz çalışıyoruz, Turkish_CI_AS ile Latin1_General_BIN2 arasında özellikle i/ı konusu küçük gibi görünse de işi bozabiliyor; regex case-insensitive flag ('i') Turkish collation’da beklediğiniz davranışı vermeyebilir, o yüzden test etmeden geçmeyin.

Peki neden? Çünkü dotted/dotless I olayı klasik ve insanı en saçma yerde yakalıyor. Hani “ölür ya” dediğiniz şey var ya, işte tam orada patlıyor.

Üçüncüsü: REGEXP_MATCHES TVF’i CROSS APPLY ile kullanınca estimated row count optimizer’ı şaşırtabiliyor. Statistics yok çünkü; karmaşık sorgularda ben bazen OPTION (RECOMPILE) kullanıyorum, bazen de veriyi geçici tabloya alıp sonra tekrar join ediyorum — ikisi de duruma göre idare eder, ama körlemesine ilerlememek lazım.

Neyse, çok dağıtmadan konuyu toparlayayım. Bu başlığı daha derin anlatacağım bir takıp yazısında ele alacağım; hatta Azure Files’ın Entra-Only moduna geçişini anlattığım yazıdaki gibi, orada da benzer şekilde “yeni özellik geldi. Production’a alırken nelere dikkat etmeli” tarafını kurcalıyoruz.

Tam da öyle.

Sıkça Sorulan Sorular

T-SQL regex fonksiyonları SQL Server 2022 veya daha eski sürümlerde çalışıyor mu?

Kısaca hayır. Native regex desteği sadece SQL Server 2025 ve Azure SQL platformlarında var. 2022 veya daha eski sürümlerde hâlâ SQLCLR ya da uygulama tarafı çözümlere ihtiyacınız olacak. Eğer migrate etmek mümkün değilse, bence en azından Azure SQL Managed Instance’a geçiş planlamasını bir gözden geçirmenizi öneririm — açıkçası bu noktada beklemeye değmez.

2 MB sınırını aşan veriler üzerinde regex çalıştırabilir mıyım?

Tek bir fonksiyon çağrısında doğrudan mümkün değil. Ama yani, uygulama tarafında veya T-SQL’de SUBSTRING ile veriyi 2 MB’lık chunk’lara bölüp her birinde ayrı ayrı regex çalıştırabilirsiniz. Pratikte zaten 2 MB’lık tek bir metin parçasıyla karşılaşmak oldukça nadir bir senaryo — mesela log dosyaları bile genellikle satır bazlı işleniyor.

RE2 motoru lookahead ve lookbehind destekliyor mu?

Hayır, desteklemiyor. RE2, tasarım gereği lookahead/lookbehind ve geri referansları (backreferences) dışarıda bırakıyor. Bu aslında kasıtlı bir karar — linear time garantisini koruyabilmek için böyle yapılmış. Tecrübeme göre bu en çok şaşırılan nokta oluyor; mevcut regex’leriniz bu özellikleri kullanıyorsa, paterninizi yeniden yazmanız ya da farklı bir yaklaşım benimsemeniz gerekecek.

Performans açısından SQLCLR regex’leriyle nasıl karşılaştırılıyor?

Benim ölçümlerimde ortalama 3-5x daha hızlı. Ama tabii bu senaryoya göre değişiyor — çok kısa string’lerde fark daha az hissedilirken, büyük LOB’larda belirgin biçimde açılıyor. Asıl kazanç bence performanstan çok bakım kolaylığında: artık CLR assembly deploy, permission set yönetimi, signed assembly gibi dertlerle uğraşmıyorsunuz. Açıkçası bu bile başlı başına yeterli bir sebep.

Azure SQL Managed Instance’ta bu özellik ne zaman aktif olacak?

“Always-up-to-date” update policy ile yapılandırılmış MI instance’larında zaten aktif durumda. “SQL Server 2025” update policy kullananlar için işe CU5 rollout’uyla birlikte geliyor. Microsoft’un rollout takvimi genellikle region bazlı kademeli ilerliyor — yani West Europe veya North Europe bölgesindeyseniz büyük ihtimalle ilk dalgada alacaksınız.

Kaynaklar ve İleri Okuma

Microsoft Azure SQL Blog — Regex support for LOB types in T-SQL

Microsoft Learn — T-SQL REGEXP_LIKE Resmî Dokümantasyonu

Garip gelecek ama, Google RE2 Syntax Reference (GitHub)

SQL Server 2025’te Yenilikler — Microsoft Learn

Aşkın KILIÇ

20+ yıl deneyimli Azure Solutions Architect. Microsoft sertifikalı bulut mimari ve DevOps danışmanı. Azure, yapay zekâ ve bulut teknolojileri üzerine Türkçe teknik içerikler üretiyor.

AZ-305AZ-104AZ-500AZ-400DP-203AI-102

Bu içerik işinize yaradı mı?

Benzer içerikleri kaçırmamak için beni sosyal medyada takip edin.

← Onceki Yazi
PowerShell macOS'ta Notarize Edildi: Gatekeeper Çilesi Bitti
Sonraki Yazi →
npm Staged Publishing GA: Tedarik Zinciri Artık Daha Güvenli

Yorum Yaz

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

İçindekiler
← PowerShell macOS’ta Nota...
npm Staged Publishing GA: Teda... →