Cursor Yerine CTE Kullanımı SORUN: Müşterimizin birden çok metin verisi satırı içeren büyük bir veritabanı tablosu vardı ve metinde en sık geçen ilk üç kelimeye ilişkin bir rapor oluşturmaları gerekiyordu. Kullandıkları orijinal T-SQL kodu, tablonun her satırında dolaşmak, metni tek tek sözcüklere bölmek ve her sözcüğün tekrarını saymak için bir cursor kullanıyordu. Ancak, cursor’un önemli performans sorunlarına…
SORUN:
Müşterimizin birden çok metin verisi satırı içeren büyük bir veritabanı tablosu vardı ve metinde en sık geçen ilk üç kelimeye ilişkin bir rapor oluşturmaları gerekiyordu. Kullandıkları orijinal T-SQL kodu, tablonun her satırında dolaşmak, metni tek tek sözcüklere bölmek ve her sözcüğün tekrarını saymak için bir cursor kullanıyordu. Ancak, cursor’un önemli performans sorunlarına neden olduğunu ve iyi ayarlanmış bir sunucuda bile sorgunun tamamlanmasının birkaç dakika sürdüğünü çabucak keşfettiler. Yardım için bize ulaştılar ve cursor’u CTE kullanan daha verimli bir çözümle değiştirmenizi tavsiye ediyoruz.
Cursor – T-SQL Kodu:
İşte müşterimizin kullandığı orijinal T-SQL kodu:
DECLARE @Text NVARCHAR(MAX)
DECLARE @Word NVARCHAR(MAX)
DECLARE @Count INT
DECLARE @Words TABLE (Word NVARCHAR(MAX), Count INT)
DECLARE Cursor1 CURSOR FOR
SELECT TextColumn
FROM MyTable
OPEN Cursor1
FETCH NEXT FROM Cursor1 INTO @Text
WHILE @@FETCH_STATUS = 0
BEGIN
SET @Text = REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE
(REPLACE(REPLACE(REPLACE(
LOWER(@Text), ‘.’, ”), ‘,’, ”), ‘;’, ”), ‘:’, ”), ‘?’, ”),
‘(‘, ”), ‘)’, ”), ‘[‘, ”), ‘]’, ”), ‘!’, ”)
DECLARE Cursor2 CURSOR FOR
SELECT value
FROM STRING_SPLIT(@Text, ‘ ‘)
OPEN Cursor2
FETCH NEXT FROM Cursor2 INTO @Word
WHILE @@FETCH_STATUS = 0
BEGIN
SET @Count = (SELECT COUNT(*) FROM @Words WHERE Word = @Word)
IF @Count = 0
BEGIN
INSERT INTO @Words (Word, Count) VALUES (@Word, 1)
END
ELSE
BEGIN
UPDATE @Words SET Count = Count + 1 WHERE Word = @Word
END
FETCH NEXT FROM Cursor2 INTO @Word
END
CLOSE Cursor2
DEALLOCATE Cursor2
FETCH NEXT FROM Cursor1 INTO @Text
END
CLOSE Cursor1
DEALLOCATE Cursor1
SELECT TOP 3 Word, Count
FROM @Words
ORDER BY Count DESC
Bu kod, MyTable tablosunun her satırında döngü yapmak, metni sözcüklere bölmek ve her sözcüğün tekrarını saymak için iç içe geçmiş iki cursor kullanır. Daha önce bahsedildiği gibi, her satır için cursor’ları açma ve kapatma ek yükü nedeniyle bu yaklaşımın performansı düşüktü.
Bu sorgunun performansını artırmak için, hesaplamayı iç içe cursor’ları kullanmak yerine tek bir küme tabanlı işlemde gerçekleştirmek için bir CTE kullanılmasını önerdim. İşte bir CTE kullanan güncellenmiş T-SQL kodu:
WITH CTE AS (
SELECT REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
LOWER(TextColumn), ‘.’, ”), ‘,’, ”), ‘;’, ”), ‘:’, ”), ‘?’, ”),
‘(‘, ”), ‘)’, ”), ‘[‘, ”), ‘]’, ”), ‘!’, ”) AS CleanedText
FROM MyTable
)
SELECT TOP 3 Word, COUNT(*) AS Count
FROM (
SELECT value AS Word
FROM CTE
CROSS APPLY STRING_SPLIT(CleanedText, ‘ ‘)
) AS Words
WHERE Word != ”
GROUP BY Word
ORDER BY COUNT(*) DESC
Bu kod, metni tek tek sözcüklere bölmek, metni noktalama işaretlerini kaldırıp küçük harfe çevirerek temizlemek ve her sözcüğün tekrarını saymak için bir CTE ve STRING_SPLIT işlevini kullanır. Ortaya çıkan veriler daha sonra seçilir ve çıktısı alınır.
ÇÖZÜM
İç içe geçmiş imleçleri bir CTE ile değiştirerek müşterim için sorgu performansını önemli ölçüde iyileştirdim. Güncellenen sorgu, orijinal imleç tabanlı yaklaşım için gereken sürenin çok daha kısa bir kısmında çalıştı ve müşteri raporlarını çok daha hızlı ve verimli bir şekilde oluşturabildi.
Bu deneyim, T-SQL kodu yazarken doğru yaklaşımı seçmenin önemini ve mümkün olduğunda imleçler yerine küme tabanlı işlemleri kullanmanın faydalarını vurgulamaktadır. SQL Server motorunun gücünden yararlanarak ve bir CTE kullanarak, istenen sonuçları çok daha iyi bir performansla elde edebildik.
İşte orijinal sorguda kullanılan MyTable için örnek veriler:
CREATE TABLE MyTable (
TextColumn NVARCHAR(MAX)
);
INSERT INTO MyTable (TextColumn)
VALUES
(‘The quick brown fox jumps over the lazy dog.’),
(‘She sells seashells by the seashore.’),
(‘How much wood would a woodchuck chuck if a woodchuck could chuck wood?’),
(‘To be or not to be, that is the question.’),
(‘Now is the winter of our discontent made glorious summer by this sun of York.’),
(‘All the world”s a stage, and all the men and women merely players.’);
Size ve Veritabanlarınıza Yardımcı Olmak İçin Bekliyoruz!