4 – PL/SQL Collections and Records

PaylaşTweet about this on TwitterShare on FacebookEmail this to someoneShare on Google+Share on LinkedInShare on TumblrShare on RedditPin on PinterestDigg thisFlattr the authorShare on StumbleUponBuffer this page

Daha önce veri türlerini incelerken pl/sql de veri türlerinin scalar ve composite olarak ikiye ayrıldığını belirtmiştim ve scalar veri türlerini incelemiştik, composite veri türleri ise iki türdür; collection ve record. Bu veri türleride scalar değiştkenlerde olduğu gibi parametre olarak procedure, function gibi alt program parçalarına gönderilebilir. Burada önemli bir konu composite bir parametre alan program birimi derlenirken composite türün varlığını kontrol etmek isteyecektir, dolayısıyla parametre geçtiğimiz yerin birim composite veri türünü tanımladığımız yere ulaşabiliyor olması gerekir. DB link vb. Yapıları kullanmamız gerekebilir. PL/SQL konusu olmadığı için db link konusuna girmiyorum ancak merak edenler araştırıp ihtiyaç halinde kullanabilirler.
Collection: Collection aynı veri türünden veriler tutan element adı verilen komponentlerden oluşur. Collection’ın her bir elementinin bir index’i vardır ve bu index ile component’e ulaşılabilir. Elemente ulaşılırken Degisken_adi(index) sytax’ı kullanılır. Collection veri türünden bir değişken tanımlayabilmek için %TYPE yazımı kullanılır.
Record: Record farklı veri türlerine sahip olan alt komponentlardan oluşur ve bunlar field ismini alır. Field veri türleri farklı olabilir hatta fieldlarda composite veri türlerinden birisi olabilir. Record’un bir field’ına erişirken field’ın adı kullanılır. Degisken_Adi.Field_adi gibi. Record türünde bir değişken tanımlamak için öncelike bir RECORD type oluşturmalı ve bu type’ı kullanarak %TYPE syntax’ı ile değişken oluşturmalıyız. Aynı zamanda %ROWTYPE anahtarı ile de record tanımı yapılabilir.
Collectionlardan oluşan bir record veya recordlardan oluşan collection tanımı yapılabilir.
Collection Türleri
Collection veri türleri üç çeşittir, bunlar; Collection types-associative array, VARRAY(variable-size array) ve nested table.
Bu üç tür için kısa bir kıyaslama yapacak olursak, öncelikle kıyaslama yapılacak başlıkları açıklayalım;
Number of Elements: Collectionun kaç aden element tutabileceği bilgisidir, özellikle belirtilmiş ise o adette belirtilmemiş ise collection’ın index veri türünün izin verdiği maximum adette eleman bulunabilir demektir.
Dense or Sparse: Collection içerisindeki elementler arasında boşluk olup olmama durumudur, collection’ın verileri ilk elemandan son elemana kadar sıralı ve atlama olmayan indexler ile tutması durumu Dense, aralarda atlama olacak şekilde tutması durumu ise Sparse olarak ifade edilecektir. 4 elemanlı bir collection için elementlere erişimde kullanılan indexlerin atlama yapmaması, “1,2,3,4” şeklinde olması, atlama yapması ise “1,4,5,7” şeklinde indexlerin olmasıdır.
Uninitialized Status: Collectionlar initialize edilmeden önce iki durumda olabilir.
1-Empty Collection: Varolan ancak içerisinde element olmayan collectionlardır. “Extend” metodu kullanılarak yeni bir element eklenebilir.
2- Null Collection: Aslında varolmayan bir collectiondır, bu collection’ı kullanabilmek için öncelikle onu initialize etmeliyiz.null collectionları extend metodu ile initialize edemeyiz.
Where Defined: Bir procedure, function veya package body gibi pl/sql bloğu içerisinde tanımlanan collection local type olarak adlandırılır ve yalnızca kendi scope’u içerisinde kullanılabilir.
Package spec’inde tanımlanan collection ise public item olarak adlandırılır, DB içerisinde istenilen yerden erişilebilir duruma gelir. Package_adi.collection_adi olarak erişilebilir. Package drop edilmediği sürece kullanılabilir durumdadır.
Schema seviyesinde tanımlanan collection ise standalone type olarak adlandırılır ve “CREATE TYPE” keyword’u ile oluşturulan bu type “DROP TYPE” komutu ile silinene kadar erişilebilir durumda olur.
Bu farklı tiplerde tanımlanmış collectionlar birbirleri ile aynı elementlere sahip olsa dahi birbirlerinin yerine kullanılamaz, imzaları aynı değildir.
Can Be ADT Attribute Data Type: Bir collection’ın ADT olabilmesi için standalone type olarak tanımlanmış olması gerekmektedir.
Convertion Non-PL/SQL Types to PL/SQL Types
Farklı bir programlama dilinde kullanılan karmaşık türleri doğrudan pl/sql türlerine çevirebiliriz.
Çevrim için kullanılabilecek türler;

Bu özellikler anlamında collection türlerinin karşılaştırması şu şekildedir;

Collection Türleri

Associative Arrays
Daha önce PL/SQL table veya index-by-table olarak bilinen bu dizi türü değer-index ikililerinden oluşur. Yani tuttuğu her bir eleman için bir index değeri ve o index’in karşılığında barındırdığı bir değer. Dizide tutulan değerlere erişirken dizi_adi(index) şeklinde erişilir.
İndex değeri PLS_INTEGER olabileceği gibi VARCHAR2, STRING, LONG gibi veri türleride olabilir. Dizilerin elemanları oluşturma sıramıza göre değil index’in sıralama kriteri ne ise ona sıralı olur. Yani index PLS_INTEGER ise 1, 2, 3 şeklinde elemanlar sıralanırken, VARCHAR2 bir index kullanmış isek a, b, c şeklinde sıralamaya tabi olur.
Sıralama kriteri NLS_SORT ve NLS_COMP parametreleri ile belirlenir.
Associative diziler veritabanı tabloları gibi disk alanı kullanmazlar ve DML komutları ile manipüle edilemezler.
Örnekler üzerinden kullanımlarını inceleyelim;

2. satırda associative array type’ın nasıl tanımlandığını görebiliriz. Yine 2. Satırda dizi içerisinde tutacağımız veri türünü belirtiyoruz. Bizim dizimiz NUMBER tipinden verileri tutacak. 3. Satırda ise dizimizin index’inin ne olacağını belirtiyoruz. Index türü ise VARCHAR2 olacak. Dizi türümüzü tanımlamış olduk. Kodlarımızda bu diziyi kullanabilmek için öncelikle tanımladığımız bu türden bir değişken tanımı yapmamız gerekiyor. 5. Satırda ise bu değişkenin tanımını görebilirsiniz.
9. satırda dizimize elemanları nasıl ekleyeceğimizi görüyoruz. İndex veri türümüz VARCHAR2 olduğu için index olarak şehir isimlerini kullanabiliyoruz. 14. Satırda ise dizinin varolan bir elemanının nasıl update edilebileceğini görüyoruz, Yozgat’ın nufüsunu 50k artırdık tek satırda!
17. ve sonraki satırlarda ise bir dizinin elemanları arasında nasıl gezebileceğimizi görüyoruz. “FIRST”, “LAST” anahtarları ile dizilerin ilk ve son elemanlarına erişebiliriz. “NEXT” anahtarı ile yolladığımız index’e sahip elemandan bir sonraki elemana geçebiliriz. 22. Satırda bu geçişi görebiliriz.
Burada önemli bir noktaya dikkat çekmek istiyorum. Diziye elemanları eklerken Yozgat, Kayseri ve İstanbul sırasıyla eklemiştik ancak dizi elemanları arasında ilk elemandan başlayıp sırayla yazdırdığımızda ise ilk elemanın Kayseri olduğunu görüyoruz. Bu yukarıda bahsettiğim sıralama ölçütü ile alakalıdır. Associative arraylerde sıralama elemanları oluşturduğumuz sırayla değil index’e göre yapılır. Index’imiz Varchar2 olduğu için elemanlar alfabetik sırayla sıralandılar. Bu yüzden ilk Yozgat’ı oluşturduğumuz halde ilk eleman Kayseri oldu. Bir dikkat çekici konu ise şu; İstanbul Kayseriden önce gelmeliydi, alfabetik olarak, ancak gelmedi. Bunun nedeni ise NLS_SORT ve NLS_COMP parametresi.

Yukarıdaki sql ile bu parametrelerin değerini bulabilirsiniz. Session bazında bu parametreleri değiştirip sonucu tekrar inceleyelim, 5 ve 7. Satırlardaki iki komutu çalıştıralım ve sıralamayı Türkçe diline göre yapacağız diyerek associative array kod bloğumuzu tekrar çalıştıralım;

Gördüğünüz gibi artık Türkçe alfabe sıralamasına göre dizimiz sıralandı ve İ, K, Y harflerine göre sıralama yapıldı.
Sabit(constant) Associative Array Sabit olarak kullanılacak bir dizi tanımı yapıyorsak bu dizinin elemanlarını önceden tanımlamamız gerekmektedir. Dizimizin elemanlarını initialize eden bir metod yazıp bu metodu array tanımı yaptığımız satırda çağırmak en efektif yol olacaktır. Bir package oluşturup onun içerisinde Associative array type’ı ile initialize functionlarını yapalım(package konusuna takılmayalım ileriki yazılarda bu konuyuda işleyeceğiz, şuan sabit olarak kullanılacak dizinin nasıl initialize edileceğini inceliyoruz).


Şimdide tanımlamış olduğumuz bu associative array’i programımız içerisinde kullanalım.

Constant (yani sabit, initialize anında değerleri atanmış daha sonra değiştirilemez) bir array’i yukarıda görülebileceği üzere tanımlarken initialize fonksiyonunu çağırmamız gerekir.

İnitialize işlemini değişken tanımı sırasında değilde yukarıdaki gibi program içerisinde yapmaya çalışırsak konsolda görülen hataları alırız. Constant yani sabit olarak tanımlanmış bir değişkeni program akışında değiştirmeye izin verilmez. Aynı zamanda tanım bloğunda da constant bir değişken varsa onu initialize etmemiz gerektiği hatasını alırız.
Dizilerde indexlerin (varchar2) NLS_SORT ve NLS_COMP parametrelerine uygun olarak sıralandığını görmüştük, dizinin initialize edilmesinden sonra bu parametreler değiştirilirse (session bazlı değiştirmek) .NEXT, .LAST, .PRIOR, .FIRST gibi array fonksiyonları hata verebilir. Session içerisinde değiştirip array ile işlem yapmadan önce tekrar eski hallerine set edebiliriz.
Associative diziler genelde nispeten küçük verileri (lookup gibi) tutmak için daha efektif bir yöntemdir. Bu dizi türü schema seviyesinde tanımlanamaz dolayısıyla standalone çalışan alt uygulamalara geçemezler. Bu durumu yukarıda associative array örneklerinde yaptığımız gibi bir package aracılığıyla yapabiliriz. Package’ın spec’inde tanımlanan array’e diğer program parçacıklarından erişilebilir.
Associative diziler verileri geçici olarak tutarlar. Diziye attığımız verileri session boyunca tutmak istersek yine packagelardan yararlanabiliriz. Spec’e type tanımını yapıp package body’si içerisinde dizimize elemenları atabiliriz.
Varrays (Variable-Size Arrays)
Varray dizileri sıfırdan(empty) belirlenmiş olan adet’e kadar değişken adette eleman tutabilen dizilerdir. Empty(boş) olmayan dizinin index’i 1 den başlar. Dizinin uzunluğu belirlenmiş sınırı aşamaz ve son elemanın index’i eleman sayısına eşittir, diziden eleman silinebilir veya eklenebilir, bu durumda dizinin büyüklüğü, son elemanın index’i vs. değişecektir. Varray değişkeninin elemanlarından birinin değerine degisken_adi(index) yazımı ile erişilebilir.

Bu dizide dizi boyutu 10 olarak verilmiştir, yani 10 dan fazla elemanı olamaz. Dizi içeriğine baktığımızda ise yalnızca 6 elemana değer atanmış görünüyor. Bu durumda dizinin son elemanının index’i 6 olacaktır.
Varray değişkenleri initialize edilmeden önce null dır, initialize edilmeden kullanılamazlar.

Script’i çalıştırdığımız zaman console da yazanlar şu şekilde olacaktır;

2. satırda VARRAY Type tanımını görüyoruz, 15 karaktere kadar veri tutabilecek 4 adet elemandan oluşabilir şeklinde tanımlıyoruz. 4. Satırda tanımlamış olduğumuz VARRAY type’ı türünden bir değişken tanımlayıp constructor aracılığıyla diziye dört adet eleman tanımlıyoruz. 23, 24 ve 25. Satırlarda dizide zaten varolan indexler verdiğimiz için verileri güncelliyoruz.
VARRAY leri tutacağımız eleman sayısını bildiğimiz, çok fazla veri barındırmayan ve elemanlara daha çok ardışıl olarak erişme ihtiyacımızın olduğu durumlarda kullanmamız daha doğru olur.
Nested Tables
Belirsiz sayıda elemanı tutabileceğimiz türden bir tablodur. Nested table elemanlarınada yine degisken_adi(index) şeklinde erişilebilir. Nested table larda boyut dinamik olarak değişebilir, eleman ekleme sırasında artarken, silme sırasında azalır. Nested table değişkenlerininde initialized elilmesi gerekir. Dizi indexleri dışında bir index’e ulaşmaya çalışılırsa ORA-06533 hatası alınacaktır. Yani iki elemanlı bir diziye 3 gönderildiği zaman hata alacaktır.

2. satırda Nested table type tanımı yapılmıştır ve 5. Satırda yapılmış olan type tanımını kullanarak ekip adında bir değişken (nested_table) oluşturduk. 24. Satırda varolan bir ekip üyesi değiştirilirken 26. Satırda constructor yardımıyla ekip yeniden oluşturuluyor. Console a da bakarsanız ilk verdiğimiz değerler yazılmış, ikinci grupta 3. Nolu elemanın değiştiği görülüyor(Melih – Mehmet). En son ise ekip tamamen değiştirilmiş.

Bu ikinci örnekte ise local bir nested table yerine tüm kullanıcılara açık olacak schema seviyesinde bir nested table türünden type tanımlayalım. 18. Satırda nested table’ın bir elemanından bir sonraki elemanına nasıl geçilebileceğini görebilirsiniz.
Nested table’ın kullanılmadan önce initialize edilmesi gerektiğini belirtmiştik. 26. Satırda bunu görebiliriz. Table’a eleman eklemesek bile boş şekilde constructor’unu çağırmamız gerekiyor. 29. satırda ise nested table’a 4 eleman ekliyoruz.

Script’i çalıştırdığımız zaman 4. Satırda henüz table da eleman olmadığı için empty mesajını yazdırıyoruz. 6. Satırda ise artık table’da 4 eleman olduğu için hepsini yazdırıyoruz.
Örneklere de bakarsan array ile nested table’ın çok farkı yokmuş gibi görünüyor, en önemli farkları şu şekildedir;
– Nested table’da önceden boyut tanımlamıyoruz, eleman eklendikçe dinamik olarak büyüklüğü artabiliyor. Array ise belirlenmiş bir adet dahilinde çalışıyor. Örneklerimizdeki TYPE tanımlarında bu durumu daha iyi görebilirsiniz.
– Arraylerde verileri tutma şekli daha doğrusu indexler sıkıştırılmış yani sıralı haldedir. Nested table oluşturulma anında böyledir ancak aralardan eleman silinince bu durum değişmekte dinamik olarak sıkıştırılmamaktadır. Aşağıdaki grafikte bu durumu daha iyi anlayabiliriz.

Görebileceğimiz üzere Arrayde elemanlar ardışık olarak durmaktadır. İndexler (Idx) artarak gitmektedir ancak nested table da aradan silinen kayıtların indexleri (idx(2) ve idx(5)) kullanılmamaktadır. Nested table’ın bu indexlerine erişmeye çalışıldığı zaman hata alırız.
– Element sayısı belirli değil ise
– İndex değerleri sıralı(ardışık) değil ise
– Bütün elementlerin değilde yukarıdaki grafik gibi aradan bazı elementlerin silinmesinin veya güncellenmesinin gerekeceği durumlar var ise
array yerine nested table kullanmak daha doğru bir yaklaşım olacaktır.
Nested table verileri veritabanın otomatik olarak generate ettiği “store table” larda tutulmaktadır. Nested table değişkenine erişmek istediğimiz zaman veri tabanı nested table ile onun için üretmiş olduğu store table arasında bağlantı kurar. Bu yöntem nested table değişkenlerinin sorgulanması (query) ve kolay bir şekilde element bazlı update edilmesini sağlamaktadır.
Collection Constructors
Yukarıda örneklerde constructor kullanmıştık, ekran görüntülerindeki commentlerde de belitmiştim. Constructor sistem tarafından oluşturulmuş collection type’ı ile aynı isme sahip fonksiyondur. Constructorlar ASSOCIATIVE ARRAY ler için kullanılamazlar, yalnızca VARRAY ve NESTED TABLE için constructor kullanımı söz konusudur.

Bu fonksiyonu collection type değişkenimizi initialize etmek için kullanırız. Parametre göndermeden çağırdığımız zaman geriye initialize edilmiş boş bir değişken dönecektir, parametre yollamış isek yolladığımız parametreleri collection değişkenine doldurarak dolu bir collection değişkeni döner. Aşağıdaki örnek daha açıklayıcı olacaktır.

2. satırda type tanımını görüyoruz. 4. Satırda constructor kullanımı bulunuyor. Sistem tarafından type ile aynı isimde bir fonksiyon olduğunu belirtmiştim. 4. Satırda parametresiz çağırılan fonksiyona baktığımız zaman 2. Satırda tanımlanmış olan type ile aynı isme sahip. Veri tabanında bu isimde tanımlı bir fonksiyon var mı kontrol edelim, Sql Developerda constructor’u seçip sağ tıklayarak “Popup describe” komutunu çalıştıralım.


Böyle bir nesne tanımlı değil hatası alıyoruz. Veri tabanının otomatik olarak böyle bir fonksiyon varmış gibi davrandığını belirtmiştim.
Constructor kullanıyorum, initialize etmek gerekir filan diyorum ama bakalım VARRAY veya NESTED TABLE kullanırken constructor çağırmadan yani collection’ı initialize etmezsem ne oluyor;

Hatamız hayırlı uğurlu olsun, hata açıklamasında da gördüğümüz üzere constructor çağırmadığımız zaman collection değişkenimiz (4. Satırda tanımlanmış olan takim değişkeni) uninitialized durumda kalıyor ve sistem bu değişkene erişmeye izin vermiyor, hata fırlatıyor. Exception handling başlığında bu durumları nasıl kontrol edeceğimizi inceleyeğiz.
Multidimensional Collections
Örneklerimizde ve anlatımlarımızda hep tek boyutlu collection typeları üzerinde durduk, giriş cümlelerinde elementlerin scalar olabileceği gibi complex türlerde olabileceğini belirtmiştim. Bu durumda Bir collection type tanımlayıp bunuda başka bir collection type altında element olarak kullanırsak çok boyutlu bir değişken kullanabilir hale geliriz. Örneklerle inceleyelim;

2. satırda klasik bir varray tanımı yapıyoruz. 4. Satırda da bu type türünden bir collection değişkeni tanımlıyoruz. 6. Satırda ise elementleri 2 satırda tanımladığımız type türünden olan bir varray type tanımlıyoruz. 8. Satırda ise yeni tanımlamış olduğumuz type türünden bir değişken tanımlıyor ve constructor aracılığıyla elemanlarına değerler atıyoruz. 4 adet element eklediğimize dikkat edelim.
Bu şekilde iki boyutlu bir varray tanımı yapmış olduk, elemanlarına erişim örneği için 13. Satır’a bakalım. nva collection değişkeninin 2. Elemanı olan varray’in 3. Elemanına ulaşmaya çalışıyoruz. Oklardan da erişmek istediğimiz elemanı görebilirsiniz.
nva collection varray’i içerisine constructor ile 4 eleman eklemiştik, 15. Satırda bulunan .EXTEND komutu ile 5. Element için dizimizde yer açıyoruz. Böylece 16. Satırda nvl(5) şeklinde 5. İndex’i kullanabildik. Diğer satırlarda yapılan işlemleri comment olarak eklediğim için burada tekrar etmeyeceğim.

Bu örnekte görebileceğimiz gibi farklı collection türlerini birbiri altında kullanabiliyoruz. Örneğin 10. Satırda bir VARRAY type tanımlıyoruz. 12. Satırda ise bu tanımlamış olduğumuz varray type’ı element olarak alan NESTED table tanımlıyoruz.
Bu örneğe benzer şekilde ASSOCIATIVE ARRAY, VARRAY, NESTED TABLE türleri birbiri içerisinde dimension oluşturabilirler.
Collection Methods
Collectionlar üzerine işlem yapmamızı ve hakkında bilgi almamızı sağlayan öntanımlı metodlardır.
Metodlar şu şekildedir;

DELETE Collection element(ler) silmek için kullanılır
TRIM VARRAY veya NESTED TABLE da sondan element silmek için kullanılır
EXTEND VARRAY veya NESTED TABLE ‘ın sonuna element eklemek için kullanılır
EXISTS VARRAY veya NESTED TABLE ’ın belirtilen element’e sahip olp olmadığı (true/false)
FIRST Collection’ın ilk index’i
LAST Collection’ın son index’i
COUNT Collection içindeki element sayısı
LIMIT Collection’ın sahip olabileceği maksimum element sayısı
PRIOR Belirtilen index’in bir önceki index’i
NEXT Belirtilen index’in bir sonraki index’i

Bu metodlar collection_degisken.metod şeklinde kullanılır ve pl/sql bloklarının içerisinde kullanılabilirken SQL içerisinde kullanılamaz.
Delete
Collectiondan element silmek için kullanılır, kullanım şekilleri;
– DELETE: collectiondaki tüm elementleri siler ve collection için ayrılmış olan bellek alanı serbest bırakılır.
– DELETE(n): n indexli element’i siler. Element yoksa her hangi bir işlem yapılmaz.
– DELETE(m, n): index değerleri m..n arasında olan elementleri siler. m değeri n değerinden büyük ise her hangi bir işlem yapmaz.
DELETE(n) ve DELETE(m, n) metodları associative array ve nested table için kullanılabilir VARRAY için kullanılamazlar. Bu iki metod ile silinen elementlerin bellek alanları ise serbest bırakılmaz.

Trim
VARRAY ve NESTED TABLE collection türlerinde collection’ın elementlerinin sondan silinmesini sağlar. İki farklı kullanım şekli vardır; TRIM ve TRIM(n). TRIM collection’ın son elemanını silerken, TRIM(n) sondan n adet element’in silinmesini sağlar. Eğer collectionda silmek istediğimizden daha az kayıt varsa SUBSCRIPT_BEYOND_COUNT hatası alınır.

Nested table değişkenimiz nt 2. Satırda, 6 element ile oluşturuluyor. 4. Satırda yazdırdığımız zaman 6 eleman yazıldığını görüyoruz.5. satırda TRIM komutu ile son element’i sil diyoruz ve yazdırdığımız zaman 6. Element silinmiş ve son elemen 55 değerine sahip n(5) olmuş. 7. Satırda DELETE(4) diyerek 4. Elemanı siliyoruz ve yazdırıyoruz. Artık table’ın index değerleri 1, 2, 3, 5 oldu. TRIM ve DELETE kullanımı için kritik ve önemli olan bir nokta; 9. Satırda TRIM(2) diyerek nt nin son iki elemanını silmek isteyelim ve tekrar yazdıralım. Son durumda 1, 2, 3 nolu indexler kaldı. Yani 4 ve 5 nolu index’e sahip kayıtları sildi(6 index’ini ilk TRIM ile silmiştik). 4 nolu index’i DELETE etmiştik ancak TRIM komutu hala bu elemanı görüyor. Bunun nedeni DELETE komutu ile silinen element’in halen bellekte tutuluyor (placeholder) olması. Yani siz DELETE komutu ile bir elemanı sildiğiniz zaman tekrar aynı yeri kullanabilmeniz için o alan rezerve tutulmaktadır. TRIM ise sildiği kaydın tekrar kullanılmasına izin vermez. Örneğin nt(6) yı TRIM ile sildik, TRIM(2) derken 6 indexli kaydı görmedi.
Extend
VARRAY ve NESTED TABLE collection değişkenlerinin sonuna eleman eklemek için kullanılır. Kullanılabilmesi için collection’ın NULL olmaması gerekiyor, constructor aracılığıyle empty veya dolu olacak şekilde initialize etmeliyiz. Üç farklı kullanımı vardır;
– EXTEND: Sona null bir eleman ekler
– EXTEND(n): Sona n adet null eleman ekler.
– EXTEND(n, i): i index’ine sahip kaydın n adet kopyasını sona ekler.
EXTEND komutu collection’ın boyutuna bakarken DELETE ile silinmiş ancak bellekte halen varolan elemanlarıda değerlendirmeye alır.

6. satırda table değişkenimizin ilk elemanından (içerisinde 11 değerini tutan) iki kopya oluşturup table’ın sonuna ekliyoruz. Console ekranında ikinci kümede üç tane elemanın içeriğinin 11 olduğunu görüyoruz. 8. Satırda nt değişkenimizin 5. Elemanını silip tekrar yazdırdığımızda table indexlerinin 1,2,3,4 olduğunu görüyoruz. Bakalım 10. Satırda EXTEND komutunu çalıştırınca, yani sona bir eleman daha eklemek isteyince index değerlerimiz ne olacak. Console da görebileceğimiz üzere index 4 ten 6 ya atladı. DELETE komutunun elemanı tamamen silmediğini nested table içerisinde bıraktığını (bellek alanını boşaltmadığını) görebiliyoruz. Bu durumda nt(5) := 9999; atamasını yaparak bu elemanı tekrar kullanabiliriz.
Exists
Bu komut ile istediğimiz eleman’ın VARRAY veya NESTED TABLE içerisinde olup olmadığını kontol edebiliriz. Eleman bulunmuş ise TRUE bulunamamış ise FALSE döner.
Collection’ın eleman sayısı dışında bir index’inin kontrol edilmesi, bellekte tutulsa dahi DELETE ile silinmiş bir elemanın kontrol edilmesi hataya sebep olmaz, FALSE değerini döner.

5 adet elemanı bulunan bir nested table type, bu type’ı kullanan bir değişken (n) tanımlayalım. Daha sonra bu değişkenin 2. Elemanını silelim( DELETE). Bu değişkenin elemanlarını yazdırırken EXISTS komutu ile ilgili index’in dizide olup olmadığını kontrol ettiğimizde output ekranında görebileceğiniz üzere 2. Elaman için elemanın olmadığı mesajını görüyoruz. Aynı şekilde collection değişkenimiz 5 elemanlıydı dolayısıyla 6 ve 7. İndex içinde elemanın olmadığı mesajını aldık (n(6) does not exists).
Normalde 6 ve 7. İndex’e erişmek isteseydik daha önceki örneklerde incelediğimiz üzere hata alacaktık (SUBSCRIPT_OUTSIDE_LIMIT).
First & Last
Collection değişkenimizin ilk ve son elemanının index’ini dönerler. DELETE ile silinmiş olan elemanların indexleri ise bu komutlar tarafından hariç tutulur. Collection null ise bu komutlarda null değer döndürür.
– Associative Array: indexlerin nümerik veya alfabetik olarak sıralandığını daha önce incelemiştik. Dolayısıyla first ve last komutları pls_integer indexli collection için en küçük ve en büyük değeri döndürecektir. String bir index’e sahip collectionlar içinde alfabetik olarak ilk ve son sırada olan index değerleri dönecektir.

Elemanları sırasıyle 4, 1, 2, 3 nolu indexlere atatık ancak elemanlar pls_integer index’e sahip oldukları için kendi içlerinde otomatik olarak sıralandı, collectiondan eleman silmeden önce first ve last elemanlar 1 ve 4. Bu iki elemanı sildiğimiz zaman ise first ve last üstte görebileceğiniz gibi 2 ve 3 oldu.
– VARRAY: Varray tipi collectionlar için index değerlerinin sıkıştırıldığını incelemiştik. Dolayısıyla initialize edilmiş bir VARRAY için First komutu her zaman 1, last komutu her zaman eleman sayısına(COUNT) eşit olarak döner.

2. satırda 10 eleman alabilecek bir varray tanımladık ve bu collection’ı 15. Satırda 4 eleman ile initialize ettik. 16. Satırda first ve last komutları bize 1, 4 değerlerini dönmektedir. 17. Satırda collection’a bir eleman daha ekleyelim(extend) ve first, last index değerlerine bakalım. first değeri yine 1 olurken last değeri 5 olarak değişti. Dikkat ettiyseniz 5 nolu index içerisine her hangi bir değer atamadık yalnızca extend ile oluşturduk buna rağmen last olarak bu elemanı görebiliyoruz.

– Nested Table: Nested table collectionlarında aradan eleman silindiği zaman indexlerin sıkıştırılmadığını görmüştük. Bu nedenler collectiondan eleman silinmemiş ise first 1, last ise eleman sayısına eşit olacaktır. Aralardan kayıt silindiğinde ise bu değerler farklı olacaktır.

25. satırda collection değişkenimizi 4 elemanlı olarak initialize ediyoruz. Bu haliyle değişkenimizi yazdırdığımızda First:1, Last: 4 olarak görünüyor ve 4 adet elemanı yazdırıyoruz. 27 ve 28. Satırlarda ise collection değişkenimizden 2 v 4 nolu index’e sahip elemanları silip tekrar yazdırmak istediğimizde First:1 (çünkü bu elemana dokunmadık, silmedik), Last:3 olarak dönüyor. Takim(2) elemanını silmiştik, 15. Satırda bu elemanın silindiği (exists komutu ile) görüldüğü için IF ifadesinde Else bloğuna düşerek ekrana (sakatlandi) yazdırılıyor. Daha önce örneklerle incelediğimiz üzere bu exists komutu dışında takim(2) şeklinde değeri direkt okumaya çalışsaydık programımız hata fırlatacaktı. Bunu örneğinizde deneyerek sizde test ederseniz fırlatılan hatayı hatırlamış olursunuz.
Count
Count fonksiyonu collection değişkeni içerisindeki eleman sayısını döndürür. DELETE komutu ile silinen elemanlar bellekten silinmemiş olsalarda hariç tutulur. VARRAY collectionlarında count fonksiyonu LAST fonksiyonu ile aynı sonucu döndürür, TRIM, DELETE gibi işlemlerden etkilenir. NESTED TABLE collectionlarında ise collectiondan eleman silinmemiş ise LAST ile COUNT eşit olur, eleman silinmiş ise COUNT LAST değerinden küçük olacaktır.

Varray değişkeni 2. Satırda 10 eleman alacak şekilde açılmış ve 4 eleman ile initialize edilmiştir. Bu durumda count ve last değerleri 4. Extend edip count ve last elemanlarına baktığımız zaman (extend(3)) count ve last 7 olurken trim ile 5 eleman sildiğimizde de yine count ile last fonksiyonlarının aynı değeri (2) döndürdüğünü görüyoruz.

3. satırda nested table değişkenimizi 4 eleman ile initialize ediyoruz. Count ve last değerlerini yazdırdığımız zaman her ikisi de 4 oluyor. 11. Satırda 3. Elemanı silerek count ve last değerini yazdırdığımızda count: 3 olurken last 4 oluyor. Nested table değişkenlerinde indexlerin sıkıştırılmadığını belirtmiştik. Delete dediğimiz zaman diğer elemanların indexleri değiştirilmediği için son elemanın index’i 4 olarak kalmaya devam ediyor. 13. Satırda ise değişkene 2 eleman eklediğimizde ise count 5, last 6 değerini alıyor. 3. Elemanı sildiğimiz için n(3) e erişmek istediğimizde erişemiyoruz(daha önce bu erişme denemesinde hata aldığımızı görmüştük). N(5) ve n(6) elemanları EXTEND komutu ile collection’a eklenmişti, onların ise var olduklarını ancak içlerinin boş olduğunu görüyoruz.
Limit
Collection’ın en fazla kaç eleman alabileceğini döner. Yalnızca VARRAY için anlamlıdır, maksimum eleman tanımı yapılmamış ise NULL dönecektir.

Üstteki örnekte görebileceğiniz gibi associative array ve nested table tanımlarında maksimum eleman tanımı yapılmadığı için LIMIT değerini istediğimiz zaman NULL dönüyor. VARRAY için 6. Satırda 4 eleman tutacağını belirttik ancak yalnızca ikisi içerisine değer atadık. Dolayısıyla COUNT değeri 2 dönerken LIMIT değeri 4 dönmektedir.
Prior & Next
Bu komutlar collection elemanları üzerinde ileri geri hareket etmek için kullanılır. Örneğin 4 elemanı olan bir collection olsun ve hiç bir elemanı silinmemiş olsun. X.PRIOR(2) dediğimiz zaman 2 elemandan önceki elemanın index değeri olan 1 değeri döner. X.NEXT(2) ise kendinden sonraki index olan 3 değerini döner. Bu komutlar DELETE ile silinmiş olan elemanları atlayacaktır. Aşağıdaki örneklerde bu durum daha net anlaşılacaktır. Bu komutlar eleman bulamıyor ise NULL değer dönecektir. Yani ilk elemanın bir önceki elemanı neydi diye bakmak istersen NULL dönecektir.

Nested table değişkenimizi 4. Satırda 6 eleman ile initialize ettik. 6. Satırda 4. Elemanı sildik.
Output satırlarını inceleyelim;
Collection’ın ilk elemanının index’i 1. İlk elemandan önceki elemana erişmek istediğimiz Nt.PRIOR(1) komutu NULL dönüyor. Collectiondan 4 nolu index’i silmiştik yani eleman indexleri şuan 1, 2, 3, 5, 6 şeklinde. Dolayısıyla nt.NEXT(3) kendinden bir sonraki index olan 5 değerini, nt.PRIOR(5) ise kendinden önceki index olan 3 değerini döndürüyor. Collection’ın son elemanının index’i ise 6 dolayısıyla nt.NEXT(6) kendisinden sonra eleman olmadığı için NULL değerini dönüyor. Bir noktaya dikkat edelim, collection da olmayan 4 ve 7 nolu indexler içinde NEXT ve PRIOR komutları hata vermemekte ve uygun index değerlerini dönmektedir.

3. satırda nested table değişkenimizi 9 elemanlı olarak initialize ediyoruz. 7. Satırda da bu collectiondan 7 indexli elemanı siliyoruz. İlk cümlem next ve prior komutlarını collection elemanları üzerinde ileri geri gezinmek için kullandığımızdı, bu örnekte bu işlemi nasıl yaptığımızı inceleyeceğiz. Benim tercihim bu işlemde while döngüsü kullanmak. Tabii ki for döngüleri ile de aynı işlemleri yapabiliriz.
Baştan sona giderken ilk olarak başlangıç elemanı olan ilk index’i elde ediyoruz. 8. Satırda collection’ın ilk elemanının index’ini alıyoruz(FIRST komutu ile). İdx değişkeni ardık collection elemanlarının index’i olacak. 9. Satırda idx değerinin boş olmama şartını kontrol ediyoruz, collection boş ise 8 satırda idx boş kalacak ve while döngüsü hiç başlamayacaktı. Örneğimiz için idx 1 oluyor ve while döngüsü başlıyor. Burada kritik olan adım 12. satır, bulunduğumuz indexten sonraki index’i idx değişkenine atayıp bir sonraki elemana geçiyoruz, son elemana gelmiş isek NEXT komutu NULL döndürecek demiştik bu durumda 9. Satırdaki kontrol idx NULL olduğu için false olup while döngüsü tamamlanacaktır. Dikkat edilmesi gereken bir nokta da 7. İndex’i sildiğim için collection değişkenleri yazılırken 6. İndexten sonra 8. İndex’e geçilmiş olmasıdır.
Sonraki adım ise sondan başa doğru gitmek, 16. Satırda başlangıç noktamız olacak son elemanın index’ini alıp 17. Satırda bu değer boş değil ise döngüye başlıyoruz. 20. satırda bulunduğumuz elemanın bir öndeki index’ine geçiyoruz. Ekran çıktısına bakarsak yine silinen 7. İndex’in atlandığını 8. İndex’in bir önceki index’inin 6 olarak alındığını görüyoruz.

Record Variables

Collection type larda olduğu gibi record türünde bir type tanımlayarak bu typetan değişken oluşturarak, %ROWTYPE ile bir tablo, view veya cursor’u referans vererek veya daha önce tanımlanmış bir record’u %TYPE anahtarı ile referans vererek record tanımı yapabiliriz. Record’un alanlarına tanım sırasında default değer vermemiş isek değerleri NULL olur. %ROWTYPE vb. Kullanarak oluşturduğumuz record içinde aynı şekilde default değerler null olur, referans olarak aldığımız kaynağın default değeri record üzerine taşınmaz.
Bir sabit olarak (constant) record değişkeni tanımlıyorsak değişken tanımı sırasında initialize etmemiz gerekir.

RECORD Types
Bir blok içerisinde tanımlanmış record type’a local type, bir package spec’i içerisinde tanımlanmış record’a ise public item adını veriyoruz. Record type lar schema seviyesinde tanımlanamazlar.
Aynı fieldlara sahip olan bir local type record ile public item birbiri ile aynı değildir(imcompatible). İki farklı type olarak değerlendirirler.

2. satırda bir record type tanımı yapıyoruz. Recordda 4 filed bulunuyor, dept_id, dept_name, mgr_id, loc_id. 10. Satırda record type türünde değişkenimizi tanımlıyoruz. 12. Satırda tanım sırasında 10 değerini atadığımız field’ı okuyarak console’a yazdırıyoruz.

2. satırda ad, soyad fieldlarında oluşan bir record tanımlıyoruz, fieldların veri türleri employee tablosundan referans verilmiş, yanı ad alanı employees tablosunda bulunan first_name alanı ile aynı türden veri tutacak demektir. 8. Satırda tanımlanan record type’ın fieldlarına dikkat edelim, kisi olan field 2. Satırda tanımlanmış olan record type türünden. Telefon field’ı ise employee tablosunda bulunan phone_number alanı ile aynı özellikte. 11. Satırda kisi_rec türünden bir record type değişkeni tanımlayıp 13-15. Satırlar arasında bu değişkenin fieldlarını dolduruyoruz. “ad” field’ına nasıl eriştiğimize dikkat edelim. “baglanti” değişkeninin “kisi” field’ının “ad” alanına erişmek istediğimizi belirterek atama yapıyoruz. Telefon ise direkt olarak baglanti record değişkeni seviyesinde.
%ROWTYPE
Record type’ın fieldlarının bir veritabanı tablosu veya view’ının erişilebilir(visible) alanlarından oluşmasını istediğimiz durumlarda kullanacağımız tanımlama şeklidir. Field isimleri ve veri türleri referans olarak tanımladığımız tablo/view’ın alanları ile aynı olacaktır.

2. satırda bir record tanımlıyoruz ve fieldların “department” tablosunun alanları ile aynı olacağını belirtiyoruz. 4. Satırdan itibaren record değişkenimizin fieldlarını dolduruyoruz. Field isimlerini 2. Satırda açık açık belirtmemiştik oklardan da görebileceğiniz gibi isimleri referans verdiğimiz tablo ile aynı.
%ROWTYPE & Virtual Column
Bir tabloda virtual column varsa ve siz bu tabloya record ile insert yapmak istiyorsanız virtual column’u hariç tutarak insert ifadesinde tüm alanları açık açık yazmalısınız.
Virtual column bulunduran bir tablo oluşturalım;


2. satırda virtual column bulunduran bir tabloyu referans göstererek record değişkeni oluşturuyoruz. 8. Satırda tabloya insert yapmak istiyoruz ve hata almadan geçiyoruz. 11. Satırda yapmaya çalıştığımız insert işleminde ise “sanal sütunlarda insert işlemine izin verilmez” hatası ile karşılaşıyoruz. Virtual column bulunduran bir tabloya 8. Satırda olduğu gibi alan isimlerini açık açık yazarak insert yapmalıyız.
%ROWTYPE & Invisible Column
Rowtype ile tanımladığımız record değişkenimiz invisible column bulunduran bir tabloya referans veriyorsa invisible olan column record içerisinden erişilemez, yani record’un invisible column ile aynı isim ve türde bir field’ı olmaz. İnvisible olan column visible olarak değiştirildiğinde ise otomatik olarak record değişkeninde bu alan kullanılabilir hale gelir.
Record Değişkenlerinin Birbirine Atanması
Eğer her iki record değişkeni aynı record type’tan oluşturulmuş ise direkt olarak birbirine atama yapılabilir. Bir diğer seçenek ise iki farklı record’un bir biri ile aynı sayıda field’a sahip olması karşılık gelen fieldların veri türlerinin aynı olmasıdır. Bunun dışındaki seçeneklerde record değişkenlerinin birbirine atanması sırasında hata alınacaktır.

12. ve 13. Satırlarda iki farklı türden tanımlanmış olan record değişkenleri bulunuyor. 12. Satırda tanımlanmış olan target değişkenine tanım sırasında default olarak değer atamıştık(4 ve 5. Satırlar). Source değişkenine de 15 ve 16. Satırlarda değer atıyoruz. 19. Satırda target değişkenini source değişkeni ile dolduruyoruz. Çıktı ekranına baktığımız zaman her iki değişkeninde aynı değeri tuttuğunu görebiliriz. Burada her iki record değişkenimizde aynı sayıda field’a sahip ve aynı türden veri tuttukları için atama işlemini başarıyla yapabildik.
Select Into & Record
Select komutu ile sorgulanmış olan alanlar ile aynı sayıda ve aynı veri türünde bir record tanımı varsa sorgu sonucunu direkt olarak record değişkenimize atayabiliriz.

Record Comparison
Record değişkenleri is null, eşit, büyük vb. Karşılaştırma operatörleri ile kontrol edilemez;
IF Rec1 IS NULL
IF Rec1 = Rec2
Vb ifadeleri kullanamayız, bunun yerine custom olarak bir metod yazıp onun üzerinden bu kontrolleri yapmamız gerekir.

PaylaşTweet about this on TwitterShare on FacebookEmail this to someoneShare on Google+Share on LinkedInShare on TumblrShare on RedditPin on PinterestDigg thisFlattr the authorShare on StumbleUponBuffer this page
3 – PL/SQL Control Statements

Your comments

Loading Facebook Comments ...

Leave a Reply