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;
0 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;
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.
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.
- PRIOR; bir önceki kayda/satıra geçer.
- FIRST; ilk kayda/satıra geçer.
- LAST; son kayda/satıra geçer.
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.
- ABSOLUTE; verilen değer pozitif ise cursor’ın başından, nefatif ise sonundan itibaren değer kadar geçer.
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ımlamaDECLARE @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…
Güzel paylaşım. Emeklerinize sağlık.
teşekkürler. güzeldi.
Çok güzel bir paylaşımdı. Emeğinize sağlık. Teşekkür ederiz.
Hocam cidden efsanesiniz blog’unuzda ne ararsan var helal olsun.
o kadar yazıya kitaba baktım boyle güzel anlatım görmedim. eline yüreğine sağlık.