MS SQL Server – Cursor Nedir? Neden ve Nasıl Kullanılır?

Merhaba,

T-SQL her ne kadar bir veri tanımlama, işaretleme ve kontrol dili olsada yapısal olarak bazen var olan konseptin dışında ihtiyaçlar doğrultusunda kullanmamız gerekebilen programlama dilidir. Çekilen verileri bütünsel ve kümesel olarak her türlü işleme tabi tutabildiğimiz T-SQL konseptinde eğer ki elde edilen veri kümesi içerisinde satır satır işlem yapmamız gereken bir durum söz konusu olursa dilin yeteneği olan while döngüsünü birşekilde kullanabiliriz lakin özellikle bu işlem için geliştirilmiş Cursorları tercih etmemiz daha doğru olacaktır. O halde gelin Cursor nedir, nasıl kullanılır? inceleyelim…

Cursor Nedir?

Cursorlar, veri kümesindeki her bir veriyi adım adım bizlere getiren ve bu şekilde satırsal bazda işlem yapmamızı sağlayan yapılardır. Çalışma şekilleri varsayılan olarak ileri doğru olsada ileri ve geri olmak üzere sırasıyla tüm satırları elde etme usulüne dayanır. İleri doğru okuma işlemi yapan Cursorlar geriye doğru okuma işlemi yapanlardan kat be kat hızlı çalışmaktadırlar.

Cursor Neden Kullanılır?

Her yapının bir kullanım nedeni olduğu gibi Cursorların kullanım nedenleride aşağıdaki durumlardır;

  • Sorgu neticesinde elde edilen veri kümesinde satır satır gezinerek işlem yapmak
  • Diğer kullanıcılar tarafından yapılan değişikliklerin görünürlük seviyesini ayarlamak

için kullanılır.

Cursor Kullanımı

Bir cursor tanımını örneklendirebilmek için onu belli bir senaryoda kullanarak tanıtmak en doğrusudur. Bu sebepten dolayı Northwind veritabanında bulunan “Personeller” tablosunda her bir satır için yapmış olduğumuz aşağıdaki cursor çalışma örneğini inceleyiniz.

‘SELECT * FROM Personeller’
sorgusunun çıktısı
Adı Soyadı Ünvan
Nancy Davolio Sales Representative
Gençay Fuller Vice President, Sales
Janet Leverling Sales Representative
Margaret Peacock Sales Representative
Steven Buchanan Sales Manager
Michael Suyama Sales Representative
Robert King Sales Representative
Laura Callahan Inside Sales Coordinator
Anne Dodsworth Sales Representative
DECLARE @Adi NVARCHAR(MAX), @Soyadi NVARCHAR(MAX), @Unvan NVARCHAR(MAX)
// Cursor oluşturuluyor.
DECLARE PersonelCursor CURSOR
FOR							
// Cursor'un temsil edeceği veri kümesini getirecek olan sorguyu belirtiliyoruz.
SELECT Adi, Soyadi, Unvan FROM Personeller
// Cursor açılarak kaynak tüketimi başlatılıyor.
OPEN PersonelCursor
// Sıradaki veri yakalanıyor ve hafızaya alınıyor. O anki veri 'PersonelCursor' tarafından temsil ediliyor ve kolon değerleri ilgili değişkenlere sıralı bir şekilde atanıyor.
FETCH NEXT FROM PersonelCursor INTO @Adi, @Soyadi, @Unvan
// İşlem başarılı ise @@FETCH_STATUS değişken değeri '0' ise işlem başarılıdır ve bir sonraki kayıt var demektir.
WHILE @@FETCH_STATUS = 0
	BEGIN
		PRINT @Adi + ' ' + @Soyadi + ' ' + @Unvan
		-- Sıradaki veriye geçilir ve tekrardan cursor üzerinden kolon değerleri ilgili değişkenlere atanır.
		FETCH NEXT FROM PersonelCursor INTO @Adi, @Soyadi, @Unvan
	END
// Cursor kapatılıyor.
CLOSE PersonelCursor
// Tarafımıza ayrılan hafızayı boşaltıyoruz.
DEALLOCATE PersonelCursor

Yukarıdaki çalışmayı satır satır izah edersek eğer;

  • 3. satırda cursor tanımlıyoruz. Dikkat ederseniz eğer cursor tanımının, t-sql’de ki normal değişken tanımlarından farkı ‘@’ karakterini kullanmamaktır.
  • 4 ile 6. satırlarda veri kümemizi getirecek olan sorgu tanımlanmaktadır.
  • 8. satırda ise cursor açılarak, aktifleştiriliyor. Nihayetinde veri okuma işlemi hazır hale gelmeli ve kaynak tüketimi başlatılmalıdır.
  • 10. satırda ise ilk satır ele alınmakta ve kolon değerleri sırasıyla değişkenlere atanmaktadır.
  • 12. satırda bulunan ‘@@FETCH_STATUS‘ global değişkeni her yeni satır getirildiğinde(FETCH) güncellenir ve bize aşağıdaki dört farklı değer ile durum bildirir;
    FETCH başarılıdır. Sonraki kayıt vardır.
    -1 Kayıt bulunamadı.
    -2 İlk kaydın öncesi yahut son kaydın ötesinde olunduğunu ifade eder. Eksik satır hatasıdır.
    -9 Kayıt getirilemiyor yahut elde edilemiyor.

    Bizlerde bildirilen bu durumlara göre cursor’ı ileri ya da geri hareket ettirerek satırlar arasında dolaşırız.

  • 16. satırda ise 8. satırda olduğu gibi veri kümesindeki bir sonraki satıra geçiliyor.
  • 19. satırda ise açılan cursor kapatılmaktadır. Cursor kapatılmadığı taktirde gereksiz yere sistem kaynağından bir tüketim yapacağından dolayı kullanıldığı noktalarda olası hatalar alınabilir.
  • 21. satırda ise hafızada cursor’a ayrılan belleği boşaltıyoruz. Bu işlemi CLOSE komutu sağlamadığından dolayı DEALLOCATE komutuyla gerçekleştiriyoruz.

Cursor’ı çalıştırdığımızda aşağıdaki sonucu almaktayız;
MS SQL Server - Cursor Nedir Neden ve Nasıl Kullanılır.

Cursor Kaydırma Komutları

Cursor varsayılan olarak sadece ileriye doğru hareket eden bir yönteme sahiptir. Buna FORWARD_ONLY denmektedir. Yukarıdaki örnekte her ne kadar yazmasakta esasında ilgili cursor aşağıdaki gibi FORWARD_ONLY özelliğini ifade edecek şekilde tanımlanabilir.
MS SQL Server - Cursor Nedir Neden ve Nasıl Kullanılır.

Ayriyetten bizler cursor’ı ileri kaydırdığımız gibi geri de kaydırabilmekte ve hatta ilk ya da son kayda direkt olarak erişebilmekteyiz. Tüm bu işlemleri aşağıdaki FETCH ile kullanacağımız yan komutlar ile gerçekleştirmekteyiz;

  • NEXT; bir sonraki kayda/satıra geçer.
    MS SQL Server - Cursor Nedir Neden ve Nasıl Kullanılır.
  • PRIOR; bir önceki kayda/satıra geçer.
    MS SQL Server - Cursor Nedir Neden ve Nasıl Kullanılır.
  • FIRST; ilk kayda/satıra geçer.
  • LAST; son kayda/satıra geçer.
    v

Yukarıdaki FECTH yan komutlarına göz atarsanız eğer hepsinin cursor tanımlamasına ek olarak SCROLL ifadesinin yazıldığına dikkatinizi çekerim. (Dikkat! SCROLL, NEXT komutu dışındakilerde zorunludur)

Diğer FECTH yan komutlarımız ise;

  • RELATIVE; Cursor’ın bulunduğu kayıttan belirtilen sayı kadar ileriye ya da geriye doğru geçer.
    MS SQL Server - Cursor Nedir Neden ve Nasıl Kullanılır.
  • ABSOLUTE; verilen değer pozitif ise cursor’ın başından, nefatif ise sonundan itibaren değer kadar geçer.
    MS SQL Server - Cursor Nedir Neden ve Nasıl Kullanılır.

Cursor Türleri Nelerdir?

Cursorlar çeşitli özelliklerine göre dört farklı türe ayrılır. Şimdi gelin bu farklı cursor türlerini detaylarıyla birlikte inceleyelim.

  • Statik Cursorlar

    Static cursorlar, tempdb üzerinde geçici tablo olarak tutulan ve hiç bir şekilde değiştirilemeyen cursorlardır.

    Tanımlama

    DECLARE @Adi NVARCHAR(MAX), @Soyadi NVARCHAR(MAX), @Unvan NVARCHAR(MAX)
    DECLARE PersonelCursor CURSOR
    GLOBAL // Cursor batch dışında da kullanılabilir.
    SCROLL // Geriye doğru kaydırma yapılabilir.
    STATIC
    FOR
    SELECT Adi, SoyAdi, Unvan FROM Personeller
    OPEN PersonelCursor
    FETCH NEXT FROM PersonelCursor INTO @Adi, @Soyadi, @Unvan
    WHILE @@FETCH_STATUS = 0
    	BEGIN
    		PRINT @Adi + ' ' + @Soyadi + ' ' + @Unvan
    		FETCH NEXT FROM PersonelCursor INTO @Adi, @Soyadi, @Unvan
    	END
    CLOSE PersonelCursor
    DEALLOCATE PersonelCursor
    

    Bu cursor geçici tablo olacağından dolayı fiziksel tablodaki değişikleri yakalayamayacaktır.

  • Keyset-Driven Cursorlar

    Keyset-Driven(Anahtar Takımı İle Çalıştırılan) cursorlar, tüm satırları unique olarak tanımlayan veri kümesi sağlamaktadırlar.
    Özellikleri

    • Kullanıldığı tablo üzerinde unique index olması gerekmektedir.
    • tempdb veritabanında veri kümesi değil, sadece anahtar takımı saklanır.
    • Keyset-Driven cursorlar oluşturuldukları anda bulunan satırlardaki değişikliklere duyarlıdırlar. Lakin oluşturulduktan sonra yeni eklenen satırlara karşı duyarlı değildirler.

    ‘Peki hocam bu anahtar takımından kasıt nedir?’ sorunuzu duyar gibiyim… Bu anahtar takımı gerçek veriye ulaşmak için kullanılan yapılardır. Yani bir nevi işaretleyicilerdir. FETCH işlemi esnasında o anki veriyi/satırı elde edebilmek için bu anahtar değeri kullanılmaktadır.
    Tanımlama

    DECLARE @Adi NVARCHAR(MAX), @Soyadi NVARCHAR(MAX), @Unvan NVARCHAR(MAX)
    DECLARE PersonelCursor CURSOR
    GLOBAL
    SCROLL
    KEYSET
    FOR
    SELECT Adi, SoyAdi, Unvan FROM Personeller
    OPEN PersonelCursor
    FETCH NEXT FROM PersonelCursor INTO @Adi, @Soyadi, @Unvan
    WHILE @@FETCH_STATUS = 0
    	BEGIN
    		PRINT @Adi + ' ' + @Soyadi + ' ' + @Unvan
    		FETCH NEXT FROM PersonelCursor INTO @Adi, @Soyadi, @Unvan
    	END
    CLOSE PersonelCursor
    DEALLOCATE PersonelCursor
    

    Bu cursor’ı oluşturduktan sonra yapılan satır ekleme değişiklikleri cursor tarafından yakalanmayacaktır.

  • Dinamik Cursorlar

    Dinamic cursorlar oluşturulduktan sonra fiziksel tabloda yapılan tüm aktivitelere karşı duyarlı olduklarından dolayı yapılan değişiklikler direkt olarak cursor’a da yansıyacaktır. Tablo temelinde veri duyarlılığına sahip olduklarından dolayı her FETCH komutu çalıştırıldığında SELECT ifadesini WHERE şartı ile birlikte tekrar çalıştıracak şekilde yeniden oluşmaktadırlar. Bundan dolayı tablodaki veri ve sütün genişliğine göre performanslarında olası olumsuz değişimler söz konusu olabilmektedir.

    Diğer cursor’lara nazaran fiziksel olan tempdb üzerinde çalışmaktansa RAM(geçici bellek)de çalışmakta ve böylece fiziksel diske nazaran daha hızlı işlem görmektedirler. İşte bundan dolayı özellikle küçük boyutlu verilerde veri duyarlılığıyla birlikte performanslı bir çalışma sergilerler.

    Tanımlama

    DECLARE @Adi NVARCHAR(MAX), @Soyadi NVARCHAR(MAX), @Unvan NVARCHAR(MAX)
    DECLARE PersonelCursor CURSOR
    DYNAMIC //Cursor'a dinamik özellik kazandırıyoruz.
    SCROLL
    GLOBAL
    FOR
    SELECT Adi, SoyAdi, Unvan FROM Personeller
    OPEN PersonelCursor
    FETCH NEXT FROM PersonelCursor INTO @Adi, @Soyadi, @Unvan
    WHILE @@FETCH_STATUS = 0
    	BEGIN
    		PRINT @Adi + ' ' + @Soyadi + ' ' + @Unvan
    		FETCH NEXT FROM PersonelCursor INTO @Adi, @Soyadi, @Unvan
    	END
    CLOSE PersonelCursor
    DEALLOCATE PersonelCursor
    

Cursor’da Sütun Güncelleme(FOR UPDATE)

Ana tabloda cursor içerisinde kullanılan kolonlardan herhangi birinde bir güncelleme söz konusu olursa eğer bunu FOR UPDATE ile belirterek aşağıdaki gibi güncellemeyi ilgili cursor’a yansıtabilmekteyiz.

Burada sadece belirtilen sütunların güncelleneceğine dikkatinizi çekerim. Aksi taktirde belirtilmeyen kolonlar read only olacak ve herhangi bir müdahale edildiğinde hata ile karşılaşılacaktır.

DECLARE @Adi NVARCHAR(MAX), @Soyadi NVARCHAR(MAX), @Unvan NVARCHAR(MAX)
DECLARE PersonelCursor CURSOR
FOR
SELECT Adi, SoyAdi, Unvan FROM Personeller
FOR UPDATE OF Adi, Soyadi
OPEN PersonelCursor
FETCH NEXT FROM PersonelCursor INTO @Adi, @Soyadi, @Unvan
WHILE @@FETCH_STATUS = 0
	BEGIN
		UPDATE Personeller SET Adi = 'Hüseyin', SoyAdi = 'Kamburoğulları' WHERE CURRENT OF PersonelCursor
		PRINT @Adi + ' ' + @Soyadi + ' ' + @Unvan
		FETCH NEXT FROM PersonelCursor INTO @Adi, @Soyadi, @Unvan
	END
CLOSE PersonelCursor
DEALLOCATE PersonelCursor

Vee böylece Transact SQL’de bizlere manevra kazandıran mühim ve kritik olan Cursor yapılanmasını detaylıca incelemiş ve irdelemiş bulunmaktayız.

İlgilenenlerin faydalanması dileğiyle…
Sonraki yazılarımda görüşmek üzere…
İyi çalışmalar…

Bunlar da hoşunuza gidebilir...

1 Cevap

  1. Recep dedi ki:

    Güzel paylaşım. Emeklerinize sağlık.

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir

*