26 Şubat 2009 Perşembe

TRUNC() Performansı

Bazen select cümlesi yazarken "between" koşulunu kullanırız.
Eğer "between" ifadesi ile beraber tarih türünde field kullanıyorsak
genellikle TRUNC() fonksiyonu kullanmayı tercih ederiz.

IFS ekranlarını incelediğimizde bu konuyla ilgili, performans noktasında
önemli fark yaratan bir çözüm vardır.

Aşağıda aynı query inin 2 versiyonu gözükmektedir. İlki trunc() fonksiyonunu
kullanır ve 20 dk. kadar sürer. 2. versiyona cevap süresi 1 dk. ya iner.
Tabi bu süreler kayıt sayınıza bağlı değişecektir.

--- 1 ------- 20 dakika ---------------------
trunc(f.promised_delivery_date) BETWEEN xbegin_ AND xend_
--- 1 ------- 20 dakika---------------------

--- 2 ------- < 1 dakika ---------------------
f.promised_delivery_date BETWEEN xbegin_ AND xend_ + ( 1 - 1/ ( 60*60*24 ) )
--- 2 ------- < 1 dakika ---------------------

Bir başka değişle, TRUNC() eşitliğin solunda kullanılırsa
indeks kullanımını iptal eder.

..Önerilmez
****************************************
WHERE TRUNC(trans_date) = TRUNC(sysdate)

..Önerilir
****************************************
WHERE trans_date BETWEEN TRUNC(sysdate) AND
TRUNC(sysdate) + 0.99999

23 Şubat 2009 Pazartesi

Yeni IFS Ekranı Tasarlama (1)

IFS win32 ekranlarının geliştirmesi genellikle iki seviyede gerçekleştirilmektedir.
1) Oracle da gerekli API ve APY dosyalarının iş kuralları çerçevesinde hazırlanması.
API ve APY dosyaları admin.exe kullanılarak çalıştırıldığında oracle üzerinde
bu ekranın çalışması için ihtiyaç duyulan view ler ve package oluşturulmuş olur.
2) Centura da IFS Framework unu kullanarak ekranların oluşturulması.

Biz konumuza API ve APY geliştirme noktasından başlayalım. IFS geliştirmelerinde, gerekli olan oracle package lere esas API ve APY dosyalarının oluşturulması için Rational Rose ve Design.exe kullanılır. Önce gerekli field leri içeren ilişkisel tablolar Rational Rose da tasarlanır ve bir model olarak kaydedilir. Ardından design.exe kullanılarak Rational Rose modelinden elde edilen DDL scripleri database e gönderilir.(Böylece mesela table larımız oluşmuş olur) Yine design.exe kullanılarak API ve APY dosyaları daha sonra üzerinde değişiklik yapmak veya yeniden kullanılmak üzere istenilen klasöre alınır ve database e gönderilir.

APY dosyalar genellikle; package spec, package body ve gerekli view leri içerirler.
Admin.exe bu dosyaları yorumlayarak database e gönderir. Başka bir değişle API ve APY
dosyalar third party tool lar ile DB ye gönderilemezler.
(Örneğin toad,plsql developer)

Bu makalenin bundan sonraki bölümlerini video lar kullanarak desteklemeyi düşünüyorum. Vakit buldukça bloğa ekleyeceğim. Yorumlarınızı bekliyorum.

cRecSelExtComboBox Hakkında

IFS ekranlarında cRecSelExtComboBox combobox’lar, yeni pencere açmadan çoktan seçmeli menü açmak ve bir kayıt seçmek için kullanılırlar. Üzerlerine tıklanıldığında yeni bir küçük pencerede seçim listesi görüntülenir ve burada en soldaki sütuna tıklayarak istediğimiz kaydı seçebiliriz.



cRecSelExtComboBox nesnesi örneklerine ait açılan kutuda kendisine bağlı child nesneler görüntülenir. Görüntülenmesi istenen child a ait foundation1 dialogbox ta “Logical Parent” olarak cRecSelExtComboBox nesnesinin seçilmesi gerekir.
Aşağıdaki örnekte vendorno, ccAgrementNo nesnesine tıklanıldığında açılan pencerede görüntülenir.



cRecSelExtComboBox a tıklanıldığında açılan pencerede alanların üzerindeki başlık bilgisi text itemlardan alınmaktadır.Bu açılan pencedeki sıralama da outline daki sıralamadır. Outline da ata/Parent her zaman mirasçısından/Child yukarıda olmalıdır.



17 Şubat 2009 Salı

Centura kullanarak istenen bir quickreport nasıl açılır?

Burada gösterilen yöntemi kullanarak örneğin herhangibir ekrandan sağ Mouse ile istediğiniz
quickreport u istediğiniz parametrelerle başlatabilirsiniz. İlk yapmamız gereken quickreport umuzun “quick_report_id” değerini öğrenmektir. “select * from quick_report” sorgusu ile öğrenilebilir.



İhtiyacımız olan cMessage class ından bir değişken tanımlıyalım.



İstenilen bölgeye aşağıdaki kod yerleştirilir. Bu kod 1016 quick_report_id li raporu, belirtilen “referansno” parametresi ile başlatacaktır.

!!CB!! 160
Call msg.Construct()
Call msg.SetName( '1016' ) ! 1016 quick_report_id li raporu açacağız.
Call msg.AddAttribute( 'referansno', tbwUzmarOdemeTalimatlari.colsReferansNo ) !referansno param set ediliyor
Call InfoService.QuickReportStart( msg.Pack() ) ! quickreport u aç

16 Şubat 2009 Pazartesi

Verilen iki tarih grubunun kesişimi kaç gün ?

Bu fonsiyon başlangıç ve bitiş tarihi verilen iki
zaman aralığının kesişiminin kaç gün olduğunu döndürür.
Fonksiyonun düzgün çalışabilmesi için
başlama tarihleri, bitiş tarihlerinden küçük olmalıdır.

FUNCTION kesisen_kac_gun_var (
tarih1_bas_ DATE,
tarih1_bit_ DATE,
tarih2_bas_ DATE,
tarih2_bit_ DATE
)
RETURN NUMBER
IS
TYPE sayiarray IS TABLE OF NUMBER
INDEX BY BINARY_INTEGER;

gecici sayiarray;
top_gun NUMBER := 0;
min_ NUMBER;
max_ NUMBER;
BEGIN
IF (tarih1_bas_ IS NULL)
OR (tarih1_bit_ IS NULL)
OR (tarih2_bas_ IS NULL)
OR (tarih2_bit_ IS NULL)
THEN
RETURN 0;
END IF;

min_ :=
LEAST (tarih1_bas_, tarih2_bas_)
- TO_DATE ('01.01.2000', 'dd.mm.yyyy')
- 10; -- +-10 araligi tedbir icic genis tuttum
max_ :=
GREATEST (tarih1_bit_, tarih2_bit_)
- TO_DATE ('01.01.2000', 'dd.mm.yyyy')
+ 10;

--array ın kullanilacak kismi 0 ile set ediliyor
--dbms_output.put_line('min_: ' || min_ || ' max_ : '||max_ );
FOR i IN min_ .. max_
LOOP
gecici (i) := 0; --deger null olursa hata oluşuyor
END LOOP;

--ilk tarih araligi her gun icin -1 ile set ediliyor
FOR i IN 1 .. (tarih1_bit_ - tarih1_bas_)
LOOP
gecici ( TO_DATE (tarih1_bas_, 'dd.mm.yyyy')
- TO_DATE ('01.01.2000', 'dd.mm.yyyy')
+ i
) := -1;
END LOOP;

--bazi -1 ler 1 olarak set ediliyor
FOR i IN 1 .. (tarih2_bit_ - tarih2_bas_)
LOOP
IF gecici ( TO_DATE (tarih2_bas_, 'dd.mm.yyyy')
- TO_DATE ('01.01.2000', 'dd.mm.yyyy')
+ i
) = -1
THEN
gecici ( TO_DATE (tarih2_bas_, 'dd.mm.yyyy')
- TO_DATE ('01.01.2000', 'dd.mm.yyyy')
+ i
) := 1;
END IF;
END LOOP;

--sonuc 1 lerin toplami
FOR i IN LEAST (tarih1_bas_, tarih2_bas_)
- TO_DATE ('01.01.2000', 'dd.mm.yyyy')
- 10 .. GREATEST (tarih1_bit_, tarih2_bit_)
- TO_DATE ('01.01.2000', 'dd.mm.yyyy')
+ 10
LOOP
IF gecici (i) = 1
THEN
top_gun := top_gun + gecici (i);
END IF;
END LOOP;

RETURN top_gun;
END;

Rakamı yazıya dönüştüren oracle foksiyonu

Fonsiyon virgulden sonraki iki basamağı dikkate alır ve yuvarlama yapmaz.

Kullanımı
**************
select rakam2yazi(153922.69, 'YTL') from dual

Sonuç
**************
YÜZELLİÜÇBİNDOKUZYÜZYİRMİİKİ YTL ALTMIŞDOKUZ KURUS

create or replace FUNCTION rakam2yazi (MY_RAKAM NUMBER, PARABIRIMI VARCHAR2) RETURN VARCHAR2
AS
MY_ARTIS NUMBER(18):=0;
MY_ARA NUMBER(18):=0;
MY_UZUNLUK NUMBER(18):=0;
MY_UZUN NUMBER(18):= NVL(LENGTH(TO_CHAR(TRUNC(MY_RAKAM))), 0) + 1;
MY_DEGER NUMBER(18):= NVL(LENGTH(TO_CHAR(TRUNC(MY_RAKAM))), 0);
MY_BIRLER VARCHAR2(54):='bir iki üç dört beş altı yedi sekiz dokuz ';
MY_ONLAR VARCHAR2(54):='on yirmi otuz kırk elli altmışyetmişseksendoksan';
MY_BINLER VARCHAR2(50):='bin milyon milyar trilyon kattrilyon';
MY_YAZIL VARCHAR2(54) :=NULL;
MY_YAZIM VARCHAR2(300):=NULL;
MY_SAYI VARCHAR2(3) :=NULL;
VAR VARCHAR2(1);
MY_YAZI VARCHAR2(2000);
HATA VARCHAR2(2000);
VIRGULDEN_SONRAKI_KISIM VARCHAR2(500);
BEGIN
IF (TRUNC(MY_RAKAM) - MY_RAKAM < 0) AND (PARABIRIMI <> 'GECXXYY') THEN --eger ondalik kisim var ise
IF (PARABIRIMI = 'YTL') OR (PARABIRIMI = 'TL') THEN
VIRGULDEN_SONRAKI_KISIM := substr( trunc(MY_RAKAM * 100),length(trunc(MY_RAKAM * 100))-1, 2 );
VIRGULDEN_SONRAKI_KISIM := rakam2yazi(to_number(VIRGULDEN_SONRAKI_KISIM),'GECXXYY');
VIRGULDEN_SONRAKI_KISIM := VIRGULDEN_SONRAKI_KISIM || ' KURUS';
ELSE
VIRGULDEN_SONRAKI_KISIM := substr( trunc(MY_RAKAM * 100),length(trunc(MY_RAKAM * 100))-1, 2 );
VIRGULDEN_SONRAKI_KISIM := &PKG..rakam2yazi(VIRGULDEN_SONRAKI_KISIM,'GECXXYY');
VIRGULDEN_SONRAKI_KISIM := VIRGULDEN_SONRAKI_KISIM || ' CENT';
END IF;
END IF;
MY_YAZI := NULL;
LOOP
MY_ARTIS := MY_ARTIS + 1;
MY_ARA := 1;
MY_YAZIM := NULL;
IF MY_UZUN >= 4 THEN
MY_UZUNLUK := 3;
MY_UZUN := MY_UZUN - 3;
ELSE MY_UZUNLUK := MY_UZUN - 1;
MY_UZUN :=1;
END IF;
MY_SAYI := SUBSTR(to_char(MY_RAKAM),MY_UZUN,MY_UZUNLUK);
FOR VAR IN 1 .. MY_UZUNLUK LOOP
IF SUBSTR(MY_SAYI,MY_ARA,1) = 1 AND MY_UZUNLUK = 3 THEN
MY_YAZIM := MY_YAZIM || 'yüz';
GOTO DONGU_SONU;
END IF;
IF MY_ARTIS = 2 AND MY_SAYI = 1 THEN
GOTO DONGU_SONU;
END IF;
IF SUBSTR(MY_SAYI,MY_ARA,1) > 0 THEN
IF MY_UZUNLUK = 2 THEN MY_YAZIL := MY_ONLAR;
ELSE MY_YAZIL := MY_BIRLER;
END IF;
MY_YAZIM := MY_YAZIM || SUBSTR(MY_YAZIL,
(to_number(SUBSTR(MY_SAYI,MY_ARA,1))-1)*6+1,6);
IF MY_UZUNLUK = 3 THEN
MY_YAZIM := MY_YAZIM || 'yüz';
END IF;
END IF;
<>
MY_ARA := MY_ARA + 1;
MY_UZUNLUK := MY_UZUNLUK - 1;
END LOOP;
IF MY_DEGER >= 4 AND MY_ARTIS != 1 AND MY_SAYI != 0 THEN
MY_YAZIM:= MY_YAZIM || SUBSTR(MY_BINLER,(MY_ARTIS-1)*10+1-10,10);
END IF;
MY_YAZI := REPLACE(MY_YAZIM,' ','') || MY_YAZI;
EXIT WHEN MY_UZUN = 1;
END LOOP;
HATA:= 0;
IF PARABIRIMI = 'GECXXYY' THEN
RETURN MY_YAZI ;
ELSE
RETURN MY_YAZI || ' ' || PARABIRIMI || ' ' ||VIRGULDEN_SONRAKI_KISIM;
END IF;
EXCEPTION
WHEN OTHERS THEN RETURN MY_YAZI; --HATA := ' ';
END;

Centura Debug Kullanımı

Centura kodları içerisinde debug edilmek istenilen satır uzerinde sağ mouse a basılarak "Toggle Breakpoint" seçeneği seçilir. Seçilen satır kırmızı olacaktır. Program akışı bu kırmızı satırlara denk geldiğinde durur ve satır satır işletmenize olanak tanınır.

Debug anında Tools/Expression menusünü seçtiğinizde gelen küçük pencerede, yazdığınız değişkenlerin çalışma anında içerdikleri değerleri veya bir fonksiyonun geri dönüş değerini yine çalışma anında inceleyebiliriz.

Unutulmaması gereken bir nokta isimlendirmede centura küçük büyük harf duyarlı davranır. Yani "benimdegisken" isimli değişken ve 'Benimdegisken' isimli değişken farklı değişkenleri ifade eder.

Konu ile ilgili, aşağıdaki linkten hazırladığım videoyu izleyebilirsiniz.

http://rapidshare.com/files/198804673/centura_debug.rar.html

11 Şubat 2009 Çarşamba

Gerçekleştirilen değişiklikler üzerine

Zaman içerisinde,herhangibir geliştirme(Oracle,Delphi,Java,IFS,Centura vs)
ortamında iş ihtiyaçları paralelinde pek çok
değişiklik gerçekleştiriyoruz. Yoğunluğa bağlı olarakta,
bir süre sonra bu değişiklikleri kolay hatırlayamaz
olmamız kaçınılmaz bir durumdur.
Bu sorunun çözümü ile ilgili benim yontemim
her değişikliğe unique bir kod verme ve bu değişiklikleri
bu kodlar vasıtasıyla izlemeye dayanıyor.
Pek çok durumda, pek çok programcının karşılaştığını düşündüğüm,
şu durumla karşılaştım.
Bir hata mesajı ile karşılaşıyorum. Bu hata mesajını verdiren
ben olduğum halde hata mesajı ile ilgili çok az şey hatırlıyorum.
Yeniden o kodları yazarken neler düşündüğümü hatırlamaya
çalışmaya dalıyorum:) Bu durum gerçekten de çok vakit kaybettirici
olabiliyor.

Çözüm ayrıntılarına gelince;
benim hazırladığım her hata mesajı, hataya ait unique
bir kod ile başlıyor ve bu kod ile aynı isimde, açıklamalar içeren,
bir text dosya hazırlıyorum.
Text dosyada geliştirme zamanında almış olduğum notlar var.
Hata ile ilgili ayrıntıları tuttuğum text dosya formatına ait
template im aşağıdaki gibi.

--DOSYA BAŞLANGICI--
Amaç : xx
Tarih : xx
Değişikli yapılan DOSYA : \\dbs\ifs\dev2007\new\original\xx.apy
Değişikli yapılan API : SHOP_ORDER_OPERATION_API
Değişikli yapılan bölge : PROCEDURE Unpack_Check_Insert___
REV No : 0
REV Açıklama : xx
REV TArih : xx

NOT : Ek klasöründe yapılan uygulama ile ilgili bir resim daha var.
Arama Yardımcı Kelimeleri : ChangeOrder, Değişim Talebi

HATA Mesajları
----------------------------------------------------------------------------
UZM0023 : xxxx

UZM0023.3 : xxxx


PROCEDURE Unpack_Check_Insert___ (
attr_ IN OUT VARCHAR2,
newrec_ IN OUT &TABLE%ROWTYPE )
IS
BEGIN
.......................................
.......................................
--çeşitli plsql kodları
.......................................
.......................................
end;
--DOSYA SONU--



Bu sorunu halletmetmeye yardımcı olacak adımlardan biri de
kaynak kod aralarına eklenen comment/açıklama satırlarıdır.
Yazdığınız her turlu koda comment eklemek çok
faydalı olacaktır.

9 Şubat 2009 Pazartesi

Oracle kullanarak hata mesajı göstermek

Error_SYS.Record_General
************************

Bu procedure; APY dosyası yani oracle package body içerisinde
geçerli kayıtla ilgili belirlenen bir durumda
hata mesajı görüntülemek ve akışı durdurmak için
kullanılabilir. Çağırıldığı noktada
geçerli işlem durdurulur ve -20110 istisnası oluşur.
Örneğin; insert veya update işleminin belirli bir durumda
gerçekleşmemesi ve oluşan durumla ilgili istenen hata mesajının
görüntülenmesi için kullanılabilir.


Record_General (lu_name_ IN VARCHAR2,
err_text_ IN VARCHAR2,
p1_ IN VARCHAR2 DEFAULT NULL,
p2_ IN VARCHAR2 DEFAULT NULL,
p3_ IN VARCHAR2 DEFAULT NULL)


Parametreler
**************
lu_name_ : Çağıran konumdaki Logical unit
err_text_ : Error text tercüme edilebilir error string .
Runtime error meydana geldiğinde translate mümkündür.
p1_ : err_text_ ile belirtilen parametre içerinde ':P1' yerine gelecek text
p2_ : err_text_ ile belirtilen parametre içerinde ':P2' yerine gelecek text
p3_ : err_text_ ile belirtilen parametre içerinde ':P3' yerine gelecek text

Örnek
****************
Error_SYS.Record_General('Benim LU',
'Bu bir :P1 .',
'denemedir', NULL, NULL);


hata mesajı "Benim LU.Bu bir denemedir ." olacaktır.

Benzer sonuç "raise application error" oracle plsql komutu
kullanılarakta alınabilir fakat Error_SYS.Record_General
prosedürün kullanımı, hem kodu daha
anlaşılır kılmakta hem de mesajın değişik dillere
çevrilmesi ve başka faydalara imkan tanımaktadır.

Oracle Nasıl Nereden Başlamalı ?

Tonguç Bey bence, Oracle a nasıl nereden başlamalı ve kendimizi nasıl geliştiririz konusunda, benim söyleyebileceklerimden fazlasını
çok başarılı bir biçimde aktarmış.
Okunması gereken makaleler diye düşünüyorum.

Oracle'a nasıl başlamalı?
**************************
http://www.ceturk.com/makaleoku.asp?id=200

Oracle'a nasıl başlamalı?(devam)
********************************
http://www.ceturk.com/makaleoku.asp?id=262

3 Şubat 2009 Salı

Centura - Bir table kolonunun null olup olmadığını nasıl test ederim?

If NOT SalIsNull( colsDocClass )

Centura - İmleç bir editbox a gitsin istiyorum. Nasıl yaparım?

--setfocus
Call SalSetFocus ( dfsBenimEditBox )

Centura - Editbox a nasıl değer atarım ?

--editbox a yada item a deger atamak
Set dfsUzmTalepNo = 'bi değer'

--editbox ta bulunan değeri değişkene almak
Set sBirStringDegisken = dfsUzmTalepNo

Centura - Herhangibir nesneyi enable disable nasıl ederim?

--bir item ı enable,disable etmek, örnekteki item editbox
Call SalDisableWindow( dfsUzmTalepNo )
Call SalEnableWindow( dfsUzmTalepNo )

API ile oluşturulan viewlerdeki "comment" ler hakkında

Örnek Comment 'ler
..............................
COMMENT ON COLUMN &VIEW..TALEPNO
IS 'FLAGS=A-IU-^DATATYPE=STRING^PROMPT=TalepNo^';
COMMENT ON COLUMN &VIEW..ISEMRINO
IS 'FLAGS=A-IU-^DATATYPE=STRING^PROMPT=IsemriNo^';
COMMENT ON COLUMN &VIEW..COTYPE
IS 'FLAGS=A-IU-^DATATYPE=STRING^PROMPT=COType^';
..............................

FLAG
****************
Field lerin çalışma zamanındaki davranışları etkiler.
Örneğin LOV(List of Value) flag ı "L" ise F8 ile listeden
seçilmeye çalışıldığında bu alan görüntülenir. "-" ise
görüntülenmez.

FLAGS=

Flag diziliminde sıralama önemlidir.
Örneğin alanı 3. karakter olmalı ve
değeri "-" veya "I" olmalıdır. Her durumda toplam
karakter sayısı 5 olmak zorundadır. Bu kurallara
uymayan girişler ihmal edilir.



Primary yani unique bir key i ifade eder
Flag için olası değerler
P = Parent key
K = Key
A = Attribute


M = Zorunlu
- = Zorunlu Değil


I = Insert e izin verilir
- = Insert e izin verilmez


U = Update e izin verilir
N = Eğer NULL ise Update e izin verilir
- = Update e izin verilmez


L = LOV da görünür
- = LOV da görünmez

Flaglari gereği gibi set ettikten sonra debug consolu
açılarak sırasıyala once Database Library Cache
sonrada Client Cache refresh edilmelidir. Değişiklikler
bu işlemler ardından aktif olur.

Instance Variables/Örnek Değişkenleri

cDataField ata sınıfından türetilmiş bir nesnenin
içerisindeki değeri bir değişkene aktarmak için

Set sBirDegisken = dfBenimcDatafield

satırına benzer bir satır yazılabilir. Ancak
bu yöntem cComboBox, cRecSelExtComboBox, cListBox
ata sınıflarından türetilmiş olan nesnelerin
gözüken(seçili) değerleri okunmaya çalışıldığında
tip uyuşmazlığı hatasına yol açacaktır.

! Tip uyuşmazlığı hatası alınır.
Set sBirDegisken = cmbBenimCombo

! İşlem sonucu başarılıdır.
Set sBirDegisken = cmbBenimCombo.i_sMyValue

Burada kullanılan "i_sMyValue" özelliği
bir instance variable' dır.IFS framework
içerisindeki bazı classlar çeşitli instance
variable lara sahiptir.

Diğer instance variable' lar ve tanımlı
oldukları class listesi aşağıdadır.


Variable Name Defined in class Description
************* ****************** **************************
i_sMyValue cComboBox,
cRecSelExtComboBox,
cListBox Nesnenin içerdiği değer
i_nMyValue cPictureDataItem Nesnenin içerdiği değer
i_hWndSelf cSessionManager Nesneye ait Window handle
i_hWndParent cSessionManager Nesneye ait logical parent Window handle
i_hWndFrame cSessionManager Nesneyi içeren table window veya
dialog box için Window handle

i_sFileName, cPictureDataItem Resim ile ilgili detaylar
i_sFilePath,
i_sDisplayText,
i_bExternalStorage

Centura - Değişken Tanımlama Örnekleri

Number: nCount

Date/Time: dtBirthday

Boolean: bReturn

String: strName

Long String: strLong

Window Handle: hWndHelp

Sql Handle: hSql

Değişken etki alanı/Variable Evaluation
*******************************************
Centura, önce lokal olarak tanımlanmış değişken olup
olmadığını kontrol eder. Eğer bulamazsa daha üst
kademelerde bulmaya çalışacaktır. Arama aşağıdaki
sırada gerçekleşir.

1. Geçerli window a ait Window Variables bölümü veya
Local Variables bölümü.

2. Geçerli window un içinde oluğu window a ait
Window Variables bölümü; onunda içinde olduğu başka bir
window var ise ... böyle devam eder.

3. Application seviyesindeki global değişken
tanımlama bölümü.