SQL'de İmleç (Cursor) Kullanımı - Murat Gül Blog
SQL’de İmleç (Cursor) Kullanımı
Bilgisayar ile uğraşırken sürekli olarak bir yerlere bilgi girilmesi gerekir. Bilgi girişinde en çok kullanılan yardımcı imleç (cursor) ‘tir. Bilgisayarın o an hangi alanla ilgilendiğini gösteren imleç aynı mantıkla SQL Server üzerinde de tanımlanmıştır.
İmleçler, SQL Server üzerinden döndürülen belli bir kayıt kümesi üzerinde satır satır işlem yapabilmeye olanak sağlayan veritabanı nesneleridir.
Satır bazında işlem yapmak aslında programlama ile uğraşanlar için sıradan işlerdendir. Bu tarz bir işlem için genel olarak aşağıdaki adımlar sırayla izlenir;
Veritabanından veri yığını istemciye çekilir.
Veri üzerinde, yüksek seviyeli programlama dilleri kullanılarak yazılmış kodlar sayesinde istenilen değişiklikler yapılır. Yüksek seviyeli dillerde sunulan döngü yapıları sayesinde her kaydı tek tek incelemek işten bile değildir.
Elde edilen sonuç kümesi veritabanına gönderilir.
Burada üzerinde durulacak olan ikinci yöntem ise aynı işlemleri veritabanı sunucusu üzerinde yapmaktır.
İki farklı yöntemin de en uygun olduğu durumlar vardır. Örneğin: Satır bazında Transact-SQL ile yapılamayacak karışık işlemler istemci tarafında yüksek seviyeli bir dil ile yazılacak kodlar ile kolaylıkla yapılabilir. Ya da Transact-SQL ile yapılabilecek satır bazında işlemleri, sunucuda imleçleri kullanarak halledebilir ve bu sayede ağ trafiğinin azalması sağlanabilir.
Yüksek seviyeli dillerde sağlanan döngü yapıları yerine sunucuda imleçleri kullanılır. İmleçlerin kullanım yerlerini örnekler üzerinde incelenecektir. Örnekler için SQL Server 2000 ile gelen pubs örnek veritabanı kullanılacak. İlk örnek titles tablosunda bulunan kitapların fiyatlarını güncellemek olacaktır.
İş planına göre;
• 20$ altındaki fiyatlar için artış oranının %10
• 20$ ve üzerindeki fiyatlar için artış oranının %5
olduğu kabul edilsin. Bunun için aşağıdaki standart Transact-SQL ifadeleri kullanılabilir;
SET price = price * (price * .1)
WHERE price < 20
SET price = price * (price * .05)
WHERE price >= 20
Burada şöyle bir sorun oluşabilir. Kitap fiyatı 19.95$ ise ilk ifadede %10’luk artış uygulanacak, bu sayede 20$’ın üstüne çıktığı için ek olarak %5’lik bir artış daha uygulanacak ve istenmeyen sonuçlar oluşacaktır.
Bu sorunu aşmak için satırları tek tek ele alıp onlar üzerinde işlem yapmak üzere imleçler kullanılacaktır.
Temel bir imleç ifadesi aşağıdaki gibi yazılır.
DECLARE cr_fiyatlar CURSOR FOR
SELECT price FROM titles
FETCH NEXT FROM cr_fiyatlar
Bu ifade Sorgu Çözümleyici’de çalıştırıldığında şekilde görünene benzer bir sonuç listesi elde edilir.
İfadede kullanılan terimlere geçmeden önce, imleç kullanmadan sadece “SELECT” ifadedesi ile aynı veriler elde edilmeye çalışılsaydı nasıl bir sonuç ortaya çıkacaktı ona bakılmalıdır. Aşağıdaki şekilde, imleçleri kullanmadan, veriler aynı “SELECT” ifadesiyle ( “SELECT price FROM titles” ) seçildiğinde alınan sonuç görülebilir.
İmleç kullanıldığında birer satır içeren sonuç kümeleri döner; fakat sadece “SELECT” ifadesi kullanıldığında tüm sonuçlar tek bir küme olarak döner.
Şimdi imleç ifadesinde kullanılan terimler incelenebilir:
Bu ifade yeni bir imleç tanımlamak için kullanılır. “declare” ifadesinden sonra imlecin adı belirtilir. Addan hemen sonra gelen “cursor for” ifadesinden sonra ise imlecin hangi veriler için tanımlanacağı belirtilir.
Örnekte ismi cr_fiyatlar olan bir imleç SELECT price FROM titles ifadesi ile dönecek olan veri kümesi içinde çalışmak üzere tanımlanıyor. Bu ifade ile dönecek olan veri kümesi üzerinde, tanımlanan imleci kullanarak satır satır gezme imkânı olacaktır. İmlecin tanımlanacağı veri kümesini tanımlayan “SELECT” ifadesi içinde, “where”, “group by” ve “order by” gibi yardımcı Transact-SQL komutları da kullanılabilir.
“Open” deyimi, belirtilen imleç için bellekte yer ayırır ve ilk kullanım için gerekli ayarları yapar. Artık imleç ilk kullanım için hazırdır.
“FETCH NEXT” deyimi tek bir kaydı alır ve imlece yükler. “NEXT” ifadesi yerine PRIOR (önceki), FIRST (ilki) ve LAST (sonuncusu) gibi ifadeler de kullanılabilir.
WHILE @@FETCH_STATUS = 0 FETCH NEXT FROM cr_fiyatlar
Örnekte imleç “while” döngüsü ile kontrol edilmiştir. @@FETCH_STATUS ifadesi döngünün kontrol birimidir. “Fetch” ifadesinin sonucuna göre üç farklı değer alabilir. Bunlar ;
0: Bir önceki “fetch” ifadesi başarılı
-1: Bir önceki “fetch” ifadesi hata ile karşılaştı
-2: Son kayıt.
Örnekte WHILE @@FETCH_STATUS = 0 ifadesi ile hata olmadığı sürece bir sonraki kayıt elde edilir.
Bir imlecin içine farklı bir imleç de yerleştirilebilir. Alternatif olarak bir imleç, içinde imleç bulunan bir saklı yordam çağırabilir. Böyle bir durumda @@FETCH_STATUS değerine dikkat etmek gerekir. Çünkü bu fonksiyon döngü düzeyinde değil bağlantı düzeyinde kontrol yapar.
Kayıtlar imlece yüklendiğinde cursor lock (imleç kilidi) oluşur. Bu kilit, CLOSE ifadesine kadar serbest bırakılmaz. “Close” ifadesinden sonra imleç tekrar açılabilir. Kapatıp açınca imleç hiçbir özelliğini kaybetmiş olmaz. Yani bellekten atılmış değildir. “Close” ile kapatmaktaki amaç kilidi açmaktır.
“Close” ile imleç bellekten temizlenmez. İmlece ihtiyaç kalmadığında kaldırmak için kullanılan bellek alanı boşaltılmalı ve değişkenler yok edilmelidir. “Deallocate” bu işlemleri tek seferde yapar. Bu işlemden sonra imleç bellekten kaldırılmıştır.
İmleçler sayesinde veri kümeleri üzerinde değil, daha çok kullanılan yapısal sorgulamalara benzer şekilde satır satır işlem yapılabilir. Bu bir miktar performans kaybını beraberinde getirir; ama bazı durumlarda bu, yapılacak karmaşık işlemlerin basitleşmesinden dolayı kabul edilebilir. Buraya kadar bir imlecin genel olarak nasıl yazıldığı görüldü. Şimdi yazının başında kurulan senaryonun uygulanmasına geçilsin.
Yukarıdaki örnekte imlecin nasıl tanımlandığını görmek için sadece kayıtlar listelendi; fakat çoğu zaman imleçleri sadece verileri listemek için kullanmak performansı olumsuz etkileyecektir. İmleçler gerçekten ihtiyaç olduğunda kullanılmalıdır. Örnekte güncelle yapmak gerektiğinden, örnek ifade güncelleme yapılmak üzere aşağıdaki gibi yenilenmelidir.
DECLARE @fiyat MONEY
DECLARE @ cr_fiyatlar CURSOR
SET @ cr_fiyatlar = CURSOR FOR
OPEN @ cr_fiyatlar
FETCH NEXT FROM @ cr_fiyatlar INTO @fiyat
IF @fiyat < 20
UPDATE titles SET price = (@fiyat + (@fiyat * .1))
WHERE CURRENT OF @ cr_fiyatlar
UPDATE titles SET price = (@fiyat + (@fiyat * .05))
CLOSE @ cr_fiyatlar
DEALLOCATE @ cr_fiyatlar
Yukarıda görüldüğü gibi yazılan Transact-SQL ifadesi sayesinde istenilen sonuç elde edildi. 20$ altındaki fiyatlar %10, 20$ ve üzerindeki fiyatlar %5 arttırılmış durumda. Bu Transact-SQL ifadesinde kullanılan “SELECT” ifadeleri;
“SELECT price FROM titles”
Sadece sonucu görmek içindir. Bu ifade olmasaydı güncelleme işlemi yine yapılacak; fakat sonuçlar listelenmeyecekti.
Burada asıl dikkat edilmesi gereken “WHERE CURRENT OF” ifadesi. Bu ifade SQL Server’a o an imlecin gösterdiği kayıt ile çalışmasını söyler.
İmleçlerin performans açısından çok tercih edilmediği belirtilmişti. Bu yüzden gerekmedikçe imleç kullanılmamalıdır. Yukardaki örnek için, Transact-SQL ifadesini biraz genişleterek (örneğin “case” yapısı ya da geçici tablolar kullanarak) imleç kullanmadan aynı sonuç elde edilebilirdi.
Ubuntu VMWare tools yükleme