Typy danych w języku PL/SQL W systemie Oracle 7 język PL/QSL udostępniał trzy kategorie typów: skalarne, złożone, odwołania. W systemie Oracle 8 zdefiniowano dwie dodatkowe kategorie typów: typ LOB, typy obiektowe. Skalarne typy danych, w odróżnieniu od typów złożonych, nie posiadają składników wewnątrz typu. Typ odwołania jest wskaźnikiem do innego typu. PL/SQL oferuje różnorodne typy danych, które można podzielić na cztery obszerne klasy: skalarne typy danych, złożone typy danych, obiektowe typy danych, inne, charakterystyczne dla języka PL/SQL. Skalarne typy danych to: rodzina NUMBER i jej typy podrzędne; rodzina CHAR, w tym Natural Character i jego typy podrzędne; rodzina DATE i jej typy podrzędne; rodzina INTERVAL; rodzina danych logicznych, rodzina MSLABEL, rodzina RAW. Złożonymi typami danych są: typ rekordowy, tabela indeksowa, kolekcje tabele zagnieżdżone i tabele o zmiennym rozmiarze. Innym przedstawicielem typów danych języka PL/SQL jest rodzina typów LOB. Wśród rodziny typów odnośników wyróżniamy typ REF CURSOR, znany również jako zmienna kursora oraz typ obiektu REF. Do obiektowych typów danych możemy zaliczyć: typy obiektowe, odwołania do obiektów oraz charakterystyczne od Oracle9i obiektowe typy danych SYS.ANYTYPE, SYS.ANYDATA, SYS.ANYDATASET oraz ich rodzina. 1
Tabela 3.1. Typy danych w PL/SQL: TYPY SKALARNE Rodzina numeryczna: Rodzina znakowa: Rodzina daty (czasu): BINARY_INTEGER CHAR DATE DEC CHARACTER INTERVAL DAY TO SECOND DECIMAL LONGCLOB INTERVAL YEAR TO MONTH DOUBLE_PRECISION NCHAR TIMESTAMP FLOAT NVARCHAR2 TIMESTAMP WITH TIME ZONE INT STRING TIMESTAMP WITH LOCAL TIME ZONE INTEGER NATURAL NATURALN NUMBER VARCHAR VARCHAR2 Rodzina identyfikatora wiersza: Rodzina logiczna: NUMERIC ROWID BOOLEAN PLS_INTEGER POSITIVE UROWID POSITIVEN Rodzina ochrony: Rodzina RAW: SIMPLE_INTEGER MLSLABEL RAW SMALINT IEEE-754 BINARY_DOUBLE BINARY_FLOAT LONG RAWBLOB lub BFILE TYPY ZŁOŻONE RECORD TYP LOB BFILE TYPY ODWOŁANIA TYPY OBIEKTOWE typ_obiektowy INDEX BY TABLE NESTED TABLE VARRAY LOB CLOB NLOB REF CURSOR REF typ_obiektowy SYS.ANYTYPE SYS.ANYDATA SYS.ANYDATASET Ponieważ wszystkie predefiniowane typy języka PL/SQL są zdefiniowane w pakiecie o nazwie STANDARD, to można je wykorzystać dla każdego bloku PL/SQL. Pakiet STANDARD definiuje również wbudowane funkcje SQL oraz funkcje konwersji dostępne w języku PL/SQL. 2
Oprócz typów predefiniowanych, których klasyfikacja została przedstawiona w powyższej tabeli, wyróżniamy jeszcze w PL/SQL typy podrzędne zdefiniowane przez użytkownika. Dla każdego typu danych obowiązuje określony zestaw zasad dotyczący ich składni i stosowalności, a motor bazy danych Oracle zapewnia obsługę tych zasad. Typy skalarne Omówimy teraz każdą z rodzin typów skalarnych, które stosowanie jest dozwolone dla kolumn bazy Oracle. Jak przedstawia to Tabela 3.1, typy skalarne można podzielić na siedem rodzin: numeryczną, znakową, daty i czasu, logiczną, RAW, identyfikatora wiersza i ochrony. Rodzina numeryczna Wszystkie typy numeryczne są przeznaczone do przechowywania liczb rzeczywistych lub liczb całkowitych. Rodzina numeryczna składa się z czterech podstawowych typów: BINARY_INTEGER, typy zgodne z formatem IEEE 754, (BINARY_DOUBLE i BINARY FLOAT), NUMBER, PLS_INTEGER. Zmienne typu NUMBER mogą przechowywać zarówno liczby całkowite, jak i rzeczywiste, zaś typy numeryczne BINARY_INTEGER i PLS_INTEGER tylko liczby całkowite (w PL/SQL te dwa typy są identyczne w dokumentacji Oracle nawet typ PLS_INTEGER jest używany do opisu obu typów). Typ NUMBER Typ NUMBER służy do przechowywania wartości numerycznych (zarówno liczb całkowitych, jak i zmiennoprzecinkowych). Jest on odpowiednikiem typu danych NUMBER kolumny tabeli w bazie danych. Składnia tego typu danych jest następująca: NUMBER(P,S), gdzie P oznacza czynnik precyzji, tj. liczbę wszystkich występujących cyfr, a S czynnik skali, tj. liczbę cyfr występujących z prawej strony znaku kropki dziesiętnej. Czynnik skali może być ujemny, co oznacza, że wartość jest zaokrąglana do określonej liczby miejsc z lewej strony kropki dziesiętnej. Zarówno czynnik precyzji, jak i skali są opcjonalne. Jeżeli jednak występuje czynnik skali, to również trzeba określić czynnik precyzji. Poniższe zasady mogą być pomocne w prawidłowym deklarowaniu zmiennych liczbowych: 3
jeśli czynnik skali ma wartość większą od zera, to w ten sposób określa miejsce z prawej strony kropki dziesiętnej, w którym następuje zaokrąglenie wartości. Natomiast użycie wartości mniejszej od zera określa miejsce zaokrąglenia z lewej strony kropki dziesiętnej. jeśli czynnik skali ma wartość równa zero, to zaokrąglenie następuje do najbliższej wartości całkowitej, jeśli czynnik skali nie został określony, zaokrąglenie nie zachodzi, jeśli wartość przypisanej liczby przekracza wartość czynnika skali, wówczas wartość przechowywana jest zaokrąglana do liczby określonej przez skalę. Poniższa tabela przedstawia różne możliwości ustawień czynnika precyzji i skali: Tabela 3.0-2. Różne kombinacje ustawień czynników precyzji i skali Deklaracja wartości numerycznej Przykładowa wartość przypisania Wartość przechowywana NUMBER 1234.5678 1234.5678 NUMBER(3) 123 123 NUMBER(3) 1234 Błąd przekroczony czynnik precyzji NUMBER(4,3) 123.4567 Błąd przekroczony czynnik precyzji NUMBER(6.1) 12345.6 12345.6 NUMBER(3,-3) 1234 1000 NUMBER(3,-1) 1234 1230 W Oracle 11g zmienne tego typu mogą przechowywać liczby z przedziału od 1.0E-130 do 1.0E126. Twórcy Oracle zalecają stosowanie typu NUMBER tylko wtedy, gdy operandy lub wyniki obliczeń znajdują się w tym przedziale. Jeśli literał lub obliczona wartość wykraczają poza podany przedział, to: przypisanie literału o wartości mniejszej od minimalnej powoduje zapisanie w zmiennej typu NUMBER wartości NULL, przypisanie literału o wartości większej od maksymalnej powoduje zapisanie zgłoszenie błędu kompilacji, wynik obliczeń przekraczający wartość maksymalną powoduje zgłoszenie błędu kompilacji. Możliwość zdefiniowania podtypu pozwala w ramach danej rodziny zdefiniować takie typy, dla których można ograniczać dozwolone wartości dla zmiennych podtypu. W języku PL/SQL istnieje pewna liczba podtypów równoważnych z typem NUMBER. Zmieniają one jedynie nazwę typu danych NUMBER, ponieważ żaden z nich nie nakłada dodatkowych ograniczeń. Fakt zdefiniowania tych podtypów pozwala na zachowanie zgodności z typami danych pochodzącymi z 4
innych baz danych, a w niekiedy również wykorzystywanie alternatywnych nazw typów ma na celu zwiększenie czytelności podtypu lub programu. Przykładami równoważnych typów dla typu NUMBER są: DEC, DECIMAL, DOUBLE PRECISION, FLOAT, NUMERIC, REAL. Spośród typów wykorzystywanych do przechowywania liczb zmiennoprzecinkowych typy DEC, DECIMAL oraz NUMERIC posiadają maksymalną wartość czynnika precyzji 38 cyfr dziesiętnych. Typy DOUBLE, PRECISION oraz FLOAT mają czynnik precyzji o wartości 126 cyfr binarnych, co w przybliżeniu odpowiada 38 cyfrom dziesiętnym. Typ REAL ma czynnik precyzji o wartości 63 cyfr binarnych, co w przybliżeniu odpowiada 18 cyfrom dziesiętnym. Oprócz powyższych typów, które nie wprowadzają ograniczeń, istnieją podtypy INTEGER, INT oraz SMALLINT, które mogą służyć do przechowywania liczb całkowitych o maksymalnej wartości czynnika precyzji równej 38 cyfrom dziesiętnym. Typ BINARY_INTEGER Typ NUMBER jest przechowywany w formacie dziesiętnym, zoptymalizowanym pod względem dokładności i wydajności przechowywania. Z tego powodu operacje arytmetyczne nie mogą być wykonywane bezpośrednio na danych typu NUMBER. W celu przeprowadzenia obliczeń wykorzystujących wielkości numeryczne typy NUMBER muszą zostać zamienione na typy binarne. Jeżeli występuje wyrażenie arytmetyczne zawierające typ NUMBER, to typ NUMBER zostanie automatycznie zamieniony na typ binarny. W razie potrzeby wynik ponownie ulegnie przekonwertowaniu do typu NUMBER. Gdy występuje wartość całkowita, która nie będzie przechowywana w bazie danych, ale będzie wykorzystywana do obliczeń, wówczas można zastosować typ danych BINARY_INTEGER. Ten typ jest wykorzystywany do przechowywania wartości liczb całkowitych ze znakiem + lub -, których wartość mieści się w zakresie od 2147483647 do +2147483647. Jest on przechowywany w formacie binarnym z uzupełnieniem do 2, który jest dostępny do obliczeń bez konwersji. Większość liczników pętli jest typu BINARY_INTEGER. Podobnie jak dla typu NUMBER, również dla typu BINARY_INTEGER występują różne podtypy danych. Podtypy BINARY_INTEGER są ograniczone, co oznacza, że mogą przechowywać wartości pochodzące z ograniczonego zakresu. 5
Podtypami tymi są: Deklaracja Zakres NATURA 0...214748364 NATURALN 0...214748364 NOT NULL POSITIVE 1...214748364 POSITIVEN 1...214748364 NOT NULL SIGNTYPE -1, 0, 1 Typ PLS_INTEGER Typy PLS_INTEGER mają taki sam zakres jak typy BINARY_INTEGERS: od -2147483647 do +2147483647. Są one również przetwarzane z wykorzystaniem rdzennego formatu z uzupełnieniem do 2. Jeżeli podczas obliczeń wykorzystujących typ PLS_INTEGER zostanie przekroczony zakres typu, to pojawi się błąd. Gdy podczas obliczeń wykorzystujących typ BINARY_INTEGER zostanie przekroczony zakres typu, wtedy wynik obliczeń może zostać przypisany zmiennej typu NUMBER (która ma większy zakres) i błąd się nie pojawi. Różnicę pomiędzy tymi dwoma typami danych zaprezentowano w poniższej sesji programu SQL*Plus: SQL> 2 z_calkbin BINARY_INTEGER 3 BEGIN 4 -- Przypisanie maksymalnej wartości do zmiennej z_calkbin 5 z_calkbin:= 2147483647 6 /* Dodanie 1 do zmiennej z_calkbin (co spowoduje przepełnienie), a 7 następnie odjęcie 1. Wynik tego obliczenia mieści się w zakresie typu 8 BINARY_INTEGER, zatem błąd nie pojawi się */ 9 z_calkbin := z_calkbin + 1 1 10 END; 11 / PL/SQL procedure successfully completed SQL> 2 z_plscalk BINARY_INTEGER 3 BEGIN 4 -- Przypisanie maksymalnej wartości do zmiennej z_plscalk 5 z_plscalk:= 2147483647 6 /* Dodanie 1 do zmiennej z_plscalk (co spowoduje przepełnienie), a 7 następnie odjęcie 1. Chociaż wynik tego obliczenia mieści się w zakresie typu 8 PLS_INTEGER, to jednak wartość pośrednia nie, zatem pojawi się błąd 6
9 ORA-1426 */ 10 z_plscalk := z_plscalk + 1 1 11 END; 12 / * Błąd w linii 1 ORA-01426: nadmiar numeryczny ORA-06512: w linii 10 Typy danych zgodne z formatem IEEE 754 Liczby o pojedynczej i podwójnej precyzji zgodne z formatem IEEE 754 służą do wykonywania obliczeń naukowych. Ich definicja i implementacja wymagają rozwiązania tradycyjnych problemów związanych z przepełnieniem i nieskończonością. Rodzina znakowa Zmienne z rodziny znakowej są wykorzystywane do przechowywania ciągu znaków lub danych znakowych. Rodzina znakowa udostępnia następujące typy: VARCHAR2, CHAR, LONG oraz NCHAR i NVARCHAR2. Rodzina znakowa typ CHAR Zmienne tego typu są ciągami znaków o stałej długości. W systemie Oracle składnia deklaracji zmiennej CHAR wygląda następująco: CHAR(L [CHAR BYTE]); gdzie L oznacza maksymalny rozmiar zmiennej. Parametr CHAR lub BYTE jest wykorzystywany do zaznaczenia, czy parametr L ma być wyrażany w bajtach czy w znakach (domyślnie w bajtach). Jeśli długość ta nie jest określona, to jej wartością domyślną jest 1 bajt. W takiej sytuacji nie stosuje się również nawiasów. Ponieważ zmienne typu CHAR są ciągami znaków o stałej długości, są one w razie potrzeby dopełniane pustymi znakami do osiągnięcia określonej długości. Jednak ze względu na możliwość uzupełniania ciągu zmiennej znakami pustymi, odpowiedniość zmiennych CHAR przy porównaniach znaków nie zawsze jest spełniona. Dopuszczalny rozmiar wartości zmiennej typu CHAR wynosi 32 767 bajtów. Dopuszczalny rozmiar kolumny typu CHAR w tabeli bazy danych wynosi 4000 bajtów. Zatem jeżeli zmienna typu CHAR zawiera więcej niż 4000 bajtów jej wartość nie może zostać wprowadzona do kolumny tabeli typu CHAR, lecz może być wprowadzona do kolumny typu VARCHAR2 lub LONG o dopuszczalnym rozmiarze 2 gigabajtów lub CLOB(4 gigabajty dla danych tego typu). Analogicznie dane kolumny typu LONG lub CLOB, których wielkość nie przekracza 32767 bajtów, mogą być zapisane do zmiennej typu CHAR. Od wersji Oracle 11g zalecane jest stosowanie typu CLOB, gdyż typ LONG został zachowany tylko na zgodność z poprzednimi wersjami. 7
Typ VARCHAR2 Typ danych VARCHAR2 jest podobny do typu zmiennych bazy danych VARCHAR2. Zmienne typu VARCHAR2 mogą przechowywać ciągi znaków o zmiennej długości, z określoną maksymalną długością. Składnia deklaracji zmiennej tego typu jest następująca: VARCHAR2(L [CHAR BYTE]), gdzie L oznacza określaną maksymalną długość zmiennej. Podanie tej długości jest konieczne, ponieważ nie występuje dla niej wartość domyślna. Dopuszczalny rozmiar dla zmiennej VARCHAR2 wynosi 32767 bajtów. Długość zmiennej VARCHAR2 jest określana domyślnie w bajtach, a nie przez podanie liczby znaków. Rzeczywiste dane są zapamiętywane za pomocą określonego zestawu znaków bazy danych, np. zestawu ASCII lub EBCDIC Code Page 500. Jeżeli wśród znaków bazy danych znajdują się znaki wielobajtowe, maksymalna liczba znaków, którą może przechować zmienna typu VARCHAR2, może być mniejsza niż to wynika z jej określonej długości podanej w bajtach. Powodem tego jest ewentualne reprezentowanie pojedynczego znaku przez większą liczbę bajtów. Dlatego of Oracle 9i jest możliwe podawanie rozmiaru zmiennej typu VARCHAR2 w znakach. Uwaga. Kolumna bazy danych VARCHAR2 może przechowywać tylko 4000 bajtów. Jeżeli zmienna PL/SQL typu VARCHAR2 zawiera więcej niż 4000 bajtów, to może zostać wprowadzona do kolumny bazy danych typu LONG o dopuszczalnym rozmiarze 2 gigabajtów (4 gigabajty dla danych typu CLOB). Analogicznie dane typu LONG lub CLOB nie mogą być przydzielone zmiennej typu VARCHAR2, chyba że ich wielkość nie przekracza 32767 bajtów. Od wersji Oracle 11g zalecane jest stosowanie typu CLOB, gdyż typ LONG został zachowany tylko na zgodność z poprzednimi wersjami. Typ VARCHAR2 dynamiczny przydział pamięci Uwaga. Należy zauważyć, iż w przypadku przypisania krótkiego ciągu znaków alfanumerycznych, np. baza zmiennej typu VARCHAR2(32767), PL/SQL nie alokuje 32 kb pamięci dla zmiennej tego typu. W takim przypadku alokowane są jedynie 4 bajty pamięci dla takiej wartości tekstowej plus klika dodatkowych bajtów pamięci. Takie zachowanie jest przykładem dynamicznego przydzielania pamięci (ang. dynamic allocation). Należy zauważyć, iż alokacja dynamiczna nie zachodzi w przypadku każdej zmiennej. Jeżeli dana zmienna posiada limit długości poniżej 2000 bajtów, system Oracle 11g przypisuje jej podaną ilość wymaganej pamięci podczas wykonywania programu, np. dla VARCHAR2(300) zostanie zarezerwowanych 300 bajtów pamięci. Natomiast w przypadku, gdy programista zdefiniuje zmienną typu VARCHAR2 rozmiaru 2000 lub wyższego, silnik języka PL/SQL przydzieli tylko tyle pamięci, aby móc zarządzać wartością, co zwykle prowadzi do optymalizacji programu. Z tego powodu rozmiary długich łańcuchów alfanumerycznych o zmiennej długości należy definiować na 2000 bajtów lub większe. Trzy sposoby definiowania zmiennych typu VARCHAR2 o domyślnej wartości NULL: 8
z_zmienna1 VARCHAR2(50); -- jawnie określony rozmiar 50 bajtów z_zmienna2 VARCHAR2(50 BYTE); -- jawnie określony rozmiar 50 bajtów z_zmienna3 VARCHAR2(50 CHAR); -- jawnie określony rozmiar 50 znaków Jeśli programista używa przydziału pamięci na podstawie liczby znaków, to należy podzielić wartość 32676 przez liczbę bajtów potrzebnych do zapisania znaku, co daje maksymalny rozmiar zmiennej typu VARCHAR2 równy 16383 znaki w przypadku znaków dwubajtowych i 10922 znaki dla znaków trzybajtowych. Poniższy program ilustruje różnice w przydziale pamięci zmiennym typu CHAR i VARCHAR2: z_char CHAR(32767):= ; z_varchar2 VARCHAR2(32767):= ; BEGIN DBMS_OUTPUT.PUT_LINE(z_char ma długość = LENGTH(z_char)); DBMS_OUTPUT.PUT_LINE(z_varchar2 ma długość = LENGTH(z_varchar2)); z_varchar2:=z_varchar2 ; DBMS_OUTPUT.PUT_LINE(z_varchar2 ma długość = LENGTH(z_varchar2)); END; / Po uruchomieniu programu zostaną wyświetlone następujące wyniki: z_char ma długość = 32676 z_varchar2 ma długość = 1 z_varchar2 ma długość = 2 Dane wyjściowe sygnalizują, iż system przydzielił zmiennej typu CHAR określoną w deklaracji ilość pamięci. Z kolei zmiennej typu VARCHAR2 system dynamicznie przypisał tylko taką ilość pamięci, jaka jest wymagana do przechowywania wartości tekstowej. Typ LONG, LONG RAW Typ LONG przechowuje strumienie znaków, a typ LONG RAW binarne łańcuchy znaków. W odróżnieniu od typu LONG kolumn tabeli w bazie danych, umożliwiających przechowywanie do 2 gigabajtów danych, zmienna typu LONG i LONG RAW języka PL/SQL jest ciągiem znaków o zmiennej długości i dopuszczalnym rozmiarze 32 760 bajtów. Zmienne te są bardzo podobne do zmiennych typu VARCHAR2. Podobnie jak do nich, do zmiennej PL/SQL typu LONG nie może zostać pobrana kolumna bazy danych typu LONG, która zawiera więcej niż 32 760 bajtów. Ponieważ dopuszczalny rozmiar zmiennej PL/SQL typu LONG jest mniejszy niż dopuszczalny rozmiar kolumny bazy danych typu LONG, ta zmienna PL/SQL może być wprowadzana do kolumny bazy danych typu LONG bez żadnych ograniczeń. Tym niemniej, od Oracle 11g zalecane jest używanie w miejsce typów CLOB lub NCLOB. Ich maksymalna wielkość wynosi od 8 do 128 terabajtów. 9
Rodzina daty i czasu - typ DATE Typ DATE języka PL/SQL jest identyczny z typem DATE bazy danych. Typ DATE jest wykorzystywany do przechowywania informacji dotyczących daty i czasu (w tym: wieku, roku, miesiąca, dnia, godziny, minuty oraz sekundy). Można zmiennym tego typu przypisać dowolną datę z zakresu od 1 stycznia 4712 roku p.n.e. do 31 grudnia roku 9999 n.e. Zmienna typu DATE ma rozmiar 7 bajtów, przy czym jeden bajt jest zarezerwowany dla każdego składnika (od wieku do sekundy). W danych typu DATE nie przechowuje się części sekundy. Do danych typu DATE wartości są przypisywane za pomocą zmiennych znakowych lub innych wartości typu DATE. Zmienne znakowe są niejawnie konwertowane na daty z wykorzystaniem domyślnego formatu daty dla bieżącej sesji. Aby uzyskać większą kontrolę nad danymi w czasie przypisywania wartości danym typu DATE, można wykorzystać wbudowaną funkcje TO_DATE. Z kolei funkcja TO_CHAR może okazać się przydatna do pobierania danych ze zmiennych typu DATE. Poniżej zilustrowane zostały sposoby definiowania zmiennych typu DATE od domyślnej wartości NULL oraz o określonych wartościach: z_data1 DATE; -- niejawne przypisanie wartości NULL z_data2 DATE:=SYSDATE; -- jawne przypisanie bieżącej daty z_data3 DATE:=1-JUL-2010; -- jawne przypisanie daty 1 lipca 2010 roku Lista pól typu danych DATE Nazwa pola Przedział wartości YEAR Od 4712 do 9999 (bez roku 0) MONTH Od 01 do 12 DAY Od 01 do 31 (ograniczone według reguł kalendarza HOUR Od 00 do 23 MINUTE Od 00 do 59 SECOND Od 00 do 59 TIMEZONE_HOUR Od 12 do 14 (przedział dostosowany do czasu letniego) Rodzina identyfikatora wiersza Rodzina identyfikatora wiersza zawiera dwa typy danych: ROWID oraz UROWID. Typ ROWID Typ ROWID języka PL/SQL jest identyczny z typem pseudokolumny bazy danych ROWID. Może on przechowywać identyfikator wiersza rowid, który można traktować jako unikalny klucz każdego wiersza bazy danych. Identyfikatory wierszy rowid są przechowywane wewnętrznie jako wielkości binarne o stałej długości, zależnej od systemu operacyjnego. 10
Zazwyczaj programy PL/SQL nie tworzą identyfikatorów wierszy są one pobierane z pseudokolumny ROWID tabeli. Tak uzyskaną wartość można następnie wykorzystać w klauzuli WHERE w następnej instrukcji UPDATE lub DELETE. Typ UROWID Typ UNROWID określa tzw. uniwersalny identyfikator wiersza. Chociaż każdy wiersz w tabeli bazy danych Oracle posiada adres, to nie musi to być adres fizyczny. Przykładowo, wiersze w tabelach wykorzystujących indeksy posiadają logiczne identyfikatory wierszy, które są określane na podstawie głównego klucza tabeli. Typ danych UROWID może służyć zarówno do przechowywania fizycznych identyfikatorów wierszy (tzn. danych typu ROWID), jak i logicznych identyfikatorów wierszy ROWID przechowywanych w tabelach indeksowanych, czego nie umożliwia zwykły typ ROWID. Firma Oracle nawet zaleca wykorzystanie typu UROWID w nowych aplikacjach, ponieważ jest on bardziej uniwersalny. Typ NCHAR oraz NVARCHAR2 Typy znakowe NLS typ NCHAR i typ NVARCHAR2- zostały udostępnione od Oracle8. Typy znakowe NLS są wykorzystywane do przechowywania ciągu znaków w różnych zestawach znaków z samego języka PL/SQL. Ten zestaw jest znany jako narodowy zestaw znaków. Typy NCHAR oraz NVARCHAR2 są zdefiniowane i wykorzystywane w ten sam sposób jak typy CHAR i VARCHAR2. Określenie ich rozmiaru może się jednak różnić dla poszczególnych narodowych zestawów znaków. Rodzina logiczna Jedynym typem danych w rodzinie logicznej (boolowskiej) jest typ BOOLEAN. Zmienne logiczne są wykorzystywane w strukturach sterowania języka PL/SQL. Zmienna typu BOOLEAN może przechowywać jedynie następujące wartości: TRUE, FALSE lub NULL.. Poniższa sekcja deklaracji jest nieprawidłowa, ponieważ 0 nie jest poprawną wartością typu BOOLEAN: z_zmienna BOOLEAN:=0; Duże obiekty (typ LOB) Duże obiekty (typy LOB) obejmują cztery typy danych: BFILE, BLOB, CLOB i NCBLOB. Przykładowo, typ BFILE wskazuje plik zewnętrzny, a jego rozmiar maksymalny wynosi 4 gigabajty. Typy BLOB, CLOB i NCLOB to typy, których maksymalna wielkość wynosi od 8 do 128 terabajtów (zależy to od wartości parametru db_bloc_size). Kolumny LOB zawierają tzw. lokalizator, który wskazuje na miejsce przechowywania danych. Pełni on także funkcję wskaźnika umożliwiającego odczyt danych i ich zapis do kolumn typu LOB. 11
Konwersja pomiędzy typami danych. Konwersja jawna. Niekiedy pożądaną sytuacją jest konieczność konwersji jednego typu danych skalarnych w inny, nawet niekoniecznie należący do tej samej rodziny typów. Język PL/SQL posiada możliwość obsługi konwersji danych pomiędzy różnymi rodzinami typów wśród skalarnych typów danych. Wewnątrz rodziny typów danych można konwertować dane należące do różnych typów bez ograniczeń. Od tej reguły wyjątkiem są tylko pewne ograniczenia nałożone na zmienne, np. zmienna typu CHAR(10) nie może być przekonwertowana do zmiennej typu VARCHAR2(1) ze względu na wolny obszar w pamięci. Podobnie ograniczenia precyzji i skali mogą uniemożliwiać konwersję pomiędzy typem NUMBER(3,2), a typem NUMBER(3). W przypadkach naruszeń związanych z ograniczeniami, kompilator PL/SQL nie generuje błędu. Tym niemniej w takich sytuacjach może wystąpić błąd wykonania (w zależności od wartości konwertowanych zmiennych). Niezależnie od typów danych, wyróżniamy dwa rodzaje konwersji: jawną, niejawną. W przypadku konwersji jawnej (ang. explicit conversion) wykorzystywane są wbudowane funkcje służące do przeprowadzania konwersji. Funkcje te są dostępne zarówno w języku SQL, jak i w języku PL/SQL. Można je wykorzystywać w razie potrzeby do wykonania konwersji jawnej pomiędzy zmiennymi różnych rodzin typów danych. Poniższa tabela podaje krótki opis i przeznaczenie tych funkcji. Tabela 3.3. Funkcje konwersji typów danych języków SQL i PL/SQL Funkcja Opis Konwertowane rodziny TO_CHAR Konwertuje argumenty do typu VARCHAR2 Numeryczna, daty i czasu w zależności od opcjonalnego specyfikatora formatu TO_DATE Konwertuje argumenty do typu DATE w Znakowa zależności od opcjonalnego specyfikatora formatu TO_TIMESTAMP Konwertuje argumenty do typu TIMESTAMP w zależności od opcjonalnego specyfikatora formatu Znakowa TO_TIMESTAMP_TZ Konwertuje argumenty do typu Znakowa TIMESTAMP WITH TIMEZONE w zależności od opcjonalnego specyfikatora formatu TO_DSINTERVAL Konwertuje argumenty do typu INTERVAL Znakowa 12
TO_YMINTERVAL TO NUMBER RAWTOHEX HEXTORAW CHARTOROWID ROWIDTOCHAR DAY TO SECOND w zależności od opcjonalnego specyfikatora formatu Konwertuje argumenty do typu INTERVAL YEAR TO MONTH w zależności od opcjonalnego specyfikatora formatu Konwertuje argumenty do typu NUMBER w zależności od opcjonalnego specyfikatora formatu Konwertuje wartość RAW do szesnastkowej reprezentacji wielkości binarnej Konwertuje szesnastkową reprezentację do równoważnej wielkości binarnej Konwertuje znakową reprezentację danych typu ROWID do wewnętrznego formatu binarnego Konwertuje wewnętrzną, binarną zmienną ROWID do zewnętrznego formatu 18- znakowego Znakowa Znakowa Raw Znakowa (musi być w formacie szesnastkowym) Znakowa (musi być w 18- znakowym formacie identyfikatora wiersza - ROWID) Identyfikatora wiersza (ROWID). Niejawna konwersja typów W przypadkach, których jest to tylko możliwe, język PL/SQL dokonuje automatycznej konwersji danych należących do różnych rodzin typów danych, tzw. konwersję niejawną (ang. implicit conversion). Poniższy przykład ilustruje tego typu sytuację: z_aktualnystanmagazyn VARCHAR2(10); BEGIN SELECT stan INTO z_aktualnystanmagazyn FROM towary WHERE id_prod = 25; W bazie danych stan jest polem typu NUMBER(7,2). Ale z_aktualnystanmagazyn jest zmienną typu VARCHAR2(10). Mimo tej niezgodności typów, program PL/SQL automatycznie przekonwertuje dane numeryczne na ciąg znaków, który przydzieli zmiennej znakowej. Program PL/SQL może dokonać konwersji pomiędzy: znakami i liczbami, 13
znakami i datami. Pomimo, niewątpliwej zalety języka PL/SQL, jaką jest konwersja pomiędzy typami danych, to jednak zgodnie z dobrą praktyką programowania należy stosować funkcję konwersji jawnej, aby nie doszło do jakiś nieporozumień związanych z analizą kodu programu języka PL/SQL. Poniższy przykład jest modyfikacją ostatniego przykładu i ilustruje wykorzystanie funkcji TO_CHAR do konwersji jawnej: z_aktualnystanmagazyn VARCHAR2(10); BEGIN SELECT TO_CHAR(stan) INTO z_aktualnystanmagazyn FROM towary WHERE id_prod = 25; Zaletą takiego działania jest to, iż jawny format ciągu znaków w razie potrzeby może być również wykorzystany dla funkcji TO_CHAR. Zwiększa to czytelność programu i akcentuje konwersję typu. Deklarowanie zmiennych W momencie deklarowania zmiennej PL/SQL przydziela pewien obszar pamięci w celu przechowania wartości oraz nadaje nazwę lokacji, w której znajduje się ta zmienna. W ten sposób możliwym staje się pobieranie i zmienianie jej wartości. Ponadto deklaracja służy do określenia typu zmiennej, koniecznego w celu zweryfikowania wartości przypisywanych zmiennej w trakcie działania programu. Składnia deklaracji zmiennej ma postać: nazwa_zmiennej TYP_DANYCH [ CONSTANT ] [ := DEFAULT wartosc_poczatkowa ]; gdzie: nazwa_zmiennej jest to nazwa deklarowanej zmiennej PL/SQL musi być zgodna z zasadami nadawania nazw identyfikatorom PL/SQL; TYP_DANYCH określa rodzaj danych, przechowywanych przez zadeklarowaną zmienną; CONSTANT wstawienie tego opcjonalnego słowa kluczowego do deklaracji zmiennej powoduje, iż program nie może zmieniać wartości tej zmiennej jest ona traktowana w programie jako stała. := DEFAULT wystąpienie tego opcjonalnego wyrażenia do deklaracji zmiennej powoduje, iż zmiennej zostanie przypisana pewna wartość początkowa, różna od NULL, równa wartości tego wyrażenia. Proces ten nazywa się inicjalizacją zmiennej. W celu określenia przypisywanej 14
wartości początkowej deklarowanej zmiennej można użyć albo operatora przypisania :=, albo słowa kluczowego DEFAULT; wartosc_poczatkowa jest to literał odpowiedni do typu danych zmiennej, który jest zadeklarowany w TYP_DANYCH. Jest on używany do określenia wartości początkowej zmiennej, a jego pominięcie oznacza, iż w momencie deklaracji zmiennej została przypisana wartość NULL. wartosc_poczatkowa może być literałem, wcześniej zadeklarowaną zmienną lub wyrażeniem. Inicjalizacja zmiennej podczas jej deklarowania służy uproszczeniu kodu, gdyż często pierwszą instrukcją bloku wykonania jest właśnie przypisanie zmiennej wartości początkowej. Poniższy przykład ilustruje deklarację zmiennej bez wartości początkowej (NULL) i z przypisaną wartością początkową (różną od NULL): z_liczbazakupow NUMBER:=0; (lub z_liczbazakupow NUMBER DEFAULT 0); z_pelnedaneklienta VARCHAR2(100); Poniżej zaprezentowano przykłady efektywnych deklaracji zmiennych PL/SQL: efektywna w wykorzystaniu zmienna typu PLS_INTEGER zainicjalizowana maksymalną dopuszczalną wartością: z_maksplsint PLS_INTEGER:=2147483647; zmienna liczbowa o domyślnej wartości, która jest wynikiem działania arytmetycznego: z_maksoplatakarna NUMBER:=5*1550; zmienna logiczna o wartości początkowej, która jest wynikiem złożonego wyrażenia: z_wycofanieklienta BOOLEAN:=GREATEST(TO_NUMBER(TO_CHAR(data_faktury, YYYY ))) < TO_NUMBER(TO_CHAR(SYSDATE YYYY ))-1; wielkość, która nie podlega zmianom, i z tego powodu jest zadeklarowana jako stała, co wymaga podania wartości początkowej: z_wiekzatrudnienia CONSTANT NUMBER:=18; Ostatni przedstawiony powyżej przypadek deklaracji zmiennej, jest szczególnym rodzajem zmiennej określanej jako stała nazwana (ang. named constant). Tego typu zmienne (traktowane jako stałe) mają swoją nazwę, typ i wartość, podobnie jak inne zmienne. Jednak w przeciwieństwie do nich, wartość tego typu zmiennych musi być zainicjowana podczas deklaracji i nie można przypisać im innej wartości w dalszej części programu. 15
Deklaracje zakotwiczone Częstą sytuacją jest wykorzystywanie przez programistę zmiennych PL/SQL do przetwarzania danych przechowywanych w tabeli bazy danych. W takim przypadku zmienna powinna być tego samego typu jak typ danych kolumny tabeli i niekiedy mówi się, iż programista chce zakotwiczyć (ang. anchor) deklarację zmiennej do kolumny w bazie danych. Przykładowo, kolumna nazwa należąca do tabeli produkty przechowuje dane typu VARCHAR2(30). Dlatego też tego rodzaju zmienną można zadeklarować w następujący sposób: z_nazwa VARCHAR2(30); Choć powyższy sposób zadeklarowania zmiennej języka PL/SQL jest jak najbardziej poprawny, tym niemniej istnieje bardziej dogodny sposób jej zadeklarowania, który lepiej dopasowuje ją do typu danych kolumny tabeli bazy danych. Wynika to m.in. z faktu, iż typ danych pola nazwa w tabeli produkty może ulegać zmianom, zarówno co do rodzaju, jak i składni. Zamiast więc samodzielnie sprawdzać typy danych można w tym celu posłużyć się kompilatorem PL/SQL. Przykładowo, jeśli po dokonaniu tych zmian kolumn nazwa przechowywałaby dane typu VARCHAR2(40), to kod PL/SQL wykorzystujący tę kolumnę, aby był z nim zgodny musiałby być zmieniony w następujący sposób z_nazwa VARCHAR2(40); Tego rodzaju postępowanie w przypadku dużych rozmiarów kodu PL/SQL mogłoby być czasochłonne i powodować powstanie wielu błędów, szczególnie w przypadku modyfikacji typów danych pól w tabelach bazy danych. W takiej sytuacji zamiast dokonywania zmian typów zmiennych w istniejącym kodzie, zaleca się wykorzystanie atrybutu %TYPE. Atrybut %TYPE jest dołączany do odsyłacza kolumny tabeli lub do innej zmiennej i zwraca jej typ, np. z_nazwa towary.nazwa%type; Tego typu deklaracje zmiennych PL/SQL są określane mianem deklaracji zakotwiczonych (ang. anchored declarations). W ten sposób zastosowanie atrybutu %TYPE zapewnia, ze wartość zmiennej z_nazwa jest takiego samego typu, jak dane przechowywane w kolumnie nazwa w tabeli produkty. Typ danych jest określany za każdym razem podczas uruchamiania bloku dla anonimowych lub nazwanych bloków oraz podczas kompilacji przechowywanych obiektów (procedur, funkcji, itd.). W ten sposób wykorzystanie deklaracji zakotwiczonych ma jeszcze tę dodatkową zaletę, iż w przypadku, gdy typ danych lub długość kolumny w bazie danych ulegnie zmianie, generalnie program automatycznie dostosowuje się do nowego typu danych. 16
Uwaga. W niektórych przypadkach zmiany definicji kolumn tabeli bazy danych mogą powodować pewne problemy. Przykładowo, jeśli wziąć pod uwagę przypadek zmiany typu danych kolumny tabeli z NUMBER na DATE, to konsekwencją tego może być konieczność sprawdzenia utworzonego wcześniej kodu programu PL/SQL z wykorzystanymi deklaracjami zakotwiczonymi i dodania funkcji konwersji TO_DATE. Innym sposobem użycia atrybutu %TYPE jest również wykorzystywanie go do wcześniejszej deklaracji zmiennej PL/SQL. W przypadku wykorzystania atrybutu %TYPE dla deklaracji zmiennej lub kolumny, których wartość jest ograniczona do ustawienia NOT NULL, zwracany typ nie ma tego ograniczenia. Stosowanie atrybutu %TYPE świadczy o dobrym stylu programowania, ponieważ w ten sposób program PL/SQL staje się bardziej elastyczny. Łatwiej też go przystosować do zmieniających się definicji bazy danych. Podtypy definiowane przez użytkownika Jak wiemy ze wcześniejszych rozważań dotyczących typów predefiniowanych, podtyp jest typem danych PL/SQL, który jest opary na już istniejącym typie. Podtyp jest zaliczany do tej samej rodziny typów, do której należy jego typ bazowy. Zastosowanie podtypu w kodzie programu języka PL/SQL ma różnorakie zastosowanie, choćby, co jest chyba najbardziej oczywiste, typowi zostaje przydzielona inna nazwa, co ma wskazywać na jego specyficzne wykorzystanie. Składnia deklaracji podtypu jest następująca: SUBTYPE nowy_podtyp IS typ_oryginalny; gdzie nowy_podtyp jest nazwą nowego podtypu, a typ_oryginalny jest nazwą typu bazowego, do którego odnosi się nowo zdefiniowany podtyp. Typ, na którym opiera się nowo zdefiniowany podtyp może być zarówno uprzednio zdefiniowanym podtypem, jak i odsyłaczem %TYPE. Ilustruje to poniższy przykład: SUBTYPE t_licznik IS NUMBER; -- zdefiniowany nowy podtyp z_licznikpetlifor t_licznik; SUBTYPE t_nazwaproduktu IS produkty.nazwa%type; -- zdefiniowany nowy podtyp z_produktyspożywcze t_nazwaproduktu -- zadeklarowana zmienna podtypu W przypadku deklaracji podtypu do wersji Oracle8i istniała zasada, iż nie można ograniczać bezpośrednio podtypu w jego definicji. Poniższy blok programu ilustruje taką nieprawidłową definicję podtypu: SUBTYPE t_licznik IS NUMBER(5); 17
Okazuje się, iż można jednak poradzić sobie z tym ograniczeniem, deklarując fikcyjną zmienną pożądanego typu, która jest ograniczona, a następnie wykorzystać atrybut %TYPE w definicji podtypu. Sytuację tego typu ilustruje poniższy przykład kodu: z_zmiennafikcyjna NUMBER(5); -- zmienna fikcyjna, która nie będzie używana SUBTYPE t_licznik IS z_zmiennafikcyjna; -- podtyp, który zwraca NUMBER(5) z_licznikpetlifor t_licznik; -- zmienna ograniczona nowego podtypu Deklaracje zmiennych wykorzystujące podtyp nieograniczony w swej definicji mogą ograniczać typ, co ilustruje poniższy przykład: SUBTYPE t_liczba IS NUMBER; z_licznik t_liczba(5); -- definicja podtyp nieograniczonego -- ograniczona zmienna. Zakres i widoczność zmiennej Zakres (ang. scope) zmiennej jest fragmentem programu, w której można się do tej zmiennej odwoływać (tzn. odczytywać ją lub zmieniać jej wartość). Dla zmiennej PL/SQL zakresem jest obszar od deklaracji zmiennej do końca bloku, inaczej mówiąc blok, w którym jest deklarowana. Kiedy zmienna wychodzi poza swój zakres, wówczas język PL/SQL zwalnia pamięć wykorzystywaną do przechowywania tej zmiennej, ponieważ nie można się już do niej odwołać. Powyższą zasadę ilustruje poniższy przykład: z _ S tanmagazyn BEGIN Zakres zmiennej z _ Produkt z _ S tanmagazyn... END;... END; NUMBER; Zakres zmiennej VARCHAR 2(25); z _ Produkt W powyższym przykładzie kodu programu PL/SQL w bloku głównym (zewnętrznym) znajduje się blok zagnieżdżony (wewnętrzny). Do zmiennej z_produkt można odwołać się tylko w obrębie bloku wewnętrznego, czyli w tej części programu, gdzie ta zmienna ma swój zasięg. W razie odwołania do tej zmiennej w bloku głównym (zewnętrznym) wystąpi błąd kompilacji: PLS-00201: identyfikator z_produkt powinien być zadeklarowany. 18
Zasięg zmiennej zawiera się w bloku, w którym została ona zadeklarowana. Wyjątkiem od tej zasady jest zmienna pakietowa (ang. package-based variable). W ten sposób język PL/SQL dzięki tej właściwości pozwala na definiowanie globalnych struktur danych, które są dostępne z poziomu innych programów, uruchamianych w trakcie sesji. Przez widoczność zmiennej rozumie się tę część programu, w której zmienna może być dostępna bez konieczności określania odwołania. Jak wynika z powyższej definicji, widoczność zmiennej zawsze znajduje się wewnątrz zakresu zmiennej. Jest oczywistym fakt, iż jeżeli zmienna jest poza swoim zakresem, to nie jest widoczna. W tym momencie należy określić sposób odwoływania się w programie do zmiennej, która znajduje się w swoim zakresie, ale nie jest widoczna. Sposób ten ilustruje poniższy przykład kodu programu, w którym blok główny zawiera blok zagnieżdżony: <<e_zewn>> z_zm1 z_zm2 BEGIN... NUMBER; VARCHAR2; Zmienne z_zm1 NUMBER oraz z_zm2 są widoczne z_zm1 z_zm3 BEGIN... END;... END; DATE; VARCHAR2; Zmienne z_zm1 NUMBER oraz z_zm2 są widoczne Zmienne z_zm1, z_zm2 oraz z_zm3 są widoczne. Można odwołać się do zmiennej z_zm1 NUMBER dzięki etykiecie e_zewn.z_zm1 Powyższy przykład przedstawia blok główny PL/SQL, który zawiera blok zagnieżdżony. W obu blokach została zadeklarowana zmienna o tej samej nazwie z_zm1, choć o różnych typach. Zmienna z_zm1 typu tekstowego zadeklarowana w bloku zagnieżdżonym przysłania w tym bloku zmienną numeryczną z_zm1. A zatem choć zakres zmiennej z_zm1 typu NUMBER rozciąga się również na blok zagnieżdżony, to jest ona w nim niewidoczna. Jak wynika z powyższego przykładowego kodu PL/SQL, aby odwołać się do zmiennej w tej części programu, w której nie jest ona widoczna, blok PL/SQL powinien przede wszystkim zostać nazwany poprzez dołączenie etykiety (w tym przypadku <<e_zewn>>). Wówczas, aby odwołać się do zmiennej z_zm1 typu NUMBER, która jest zadeklarowana w bloku zewnętrznym, a nie jest widoczna w bloku zagnieżdżonym, należy zastosować następującą konstrukcję: e_zewn.z_zm1. Jeśli zatem identyfikator zmiennej poprzedzimy identyfikatorem nazwy bloku zewnętrznego, to w bloku zagnieżdżonym mamy 19
możliwość odwoływać się do zmiennych niewidocznych w tej części programu, które są oczywiście w nim dostępne. Uwaga. Jeśli nie jest to konieczne, należy unikać przykrywania widoczności zmiennych w blokach zagnieżdżonych. Wyrażenia i operatory Na przetwarzanie danych z wykorzystaniem zmiennych pozwalają operatory i wyrażenia. Operator jest słowem kluczowym lub symbolem, który jest wykorzystywany przez język do wykonywania funkcji arytmetycznych, logicznych i innych. Operatory definiują zatem, w jaki sposób wartości są przydzielane zmiennym oraz jak te wartości są później przetwarzane. Kategoria operatorów Notacja Typ operatora Znaczenie Przypisania := Binarny Zachowanie wartości Arytmetyczne ** Binarny Potęgowanie * Binarny Mnożenie, dzielenie / Binarny - Binarny Odejmowanie + Binarny Dodawanie Logiczne AND Binarny Koniunkcja Operatory relacyjne -porównania (oprócz wartości NULL) OR Binarny Alternatywa NOT Unarny Negacja = Binarny Równość <> lub!= Binarny Nierówność < Binarny Mniejszy niż > Binarny Większy niż <= Binarny Mniejszy bądź równy >= Binarny Większy bądź równy IN Binarny Zawieranie się BETWEEN AND Binarny Zawieranie w przedziale **, NOT Binarny Potęgowanie, logiczna negacja Porównania (wartości NULL) IS NULL Unarny Test na wartość NULL IS NOT NULL Unarny Test na wartość różną od NULL Tekstowe Binarny Konkatenacja ciągów znaków LIKE Binarny Pasowanie do wieloznacznika W powyższej tabeli zestawiono operatory języka PL/SQL. Kolejność ich wykonywania, określana mianem pierwszeństwa, jest analogiczna do wykonywania działań w arytmetyce i logice. Jeśli funkcje występują w tej samej linii, oznacza to, że wykonywane są z tym samym poziomem pierwszeństwa, a zatem o kolejności ich wykonania decyduje miejsce wyrażenia w linii programu. Sekwencję zmiennych i literałów oddzielonych od siebie operatorami nazywamy wyrażeniem. Wartość wyrażenia jest ustalana na podstawie wartości zmiennych składowych i literałów oraz definicji operatorów. 20
Operator przypisania Podstawowym operatorem jest przede wszystkim przypisanie. Składnia tego operatora jest następująca: zmienna := wyrażenie; gdzie zmienna jest zmienną PL/SQL, a wyrażenie jest wyrażeniem języka PL/SQL. Wielkość, która może się pojawić po lewej stronie operatora przypisania, znana jest jako wielkość lewa lvalue, a wielkość która może pojawić się po jego prawej stronie, nazywana jest wielkością prawą rvalue. Wielkość lewa lvalue musi odwoływać się do rzeczywistego miejsca pamięci, ponieważ wielkość prawa rvalue zostanie tam wpisana. Przypisania są dozwolone w dowolnej sekcji programu. Należy zaznaczyć, iż umieszczenie tej operacji w sekcji deklaracji ma zazwyczaj na celu inicjalizację wartości zmiennej przed jej użyciem w programie lub zdefiniowanie wartości, które będą wykorzystywane w programie jako stałe. Poniżej podano kilka przykładów przypisań: BEGIN END; z_tekst1 VARCHAR2(25); z_tekst2 VARCHAR2(30); z_num NUMBER; z_tekst1 := Napój pomarańczowy; z_teks2 := Firma ABC; z_num NUMBER := 12,4; W powyższym przykładzie wszystkie wielkości lewe lvalue są zmiennymi. Mechanizm PL/SQL przydziela zmiennym obszary pamięci tak, aby wartości Napój pomarańczowy, Firma ABC i - 12,4 mogły zostać do nich wprowadzone. Wielkość prawa rvalue może być zawartością obszaru pamięci (do którego odwołuje się zmienna) lub literałem. Powyższy przykład uwzględnia obydwa przypadki: Napój pomarańczowy jest literałem, a z_tekst1 zmienną. Wielkość prawa rvalue będzie odczytywana, natomiast do wielkości lewej będzie dokonywany zapis. Wszystkie wielkości lvalue są również wielkościami prawymi rvalue. W języku PL/SQL nie można wykonać jednocześnie więcej niż jednego przypisania. W odróżnieniu od takich języków jak C poniższe przypisanie jest niedozwolone: z_zm1 NUMBER; z_zm2 NUMBER; z_zm3 NUMBER; BEGIN z_zm1 := z_zm2 := z_zm3 :=0; END; 21
Wyrażenia Wyrażenia PL/SQL są wielkościami prawymi rvalue. Samo wyrażenie nie ma znaczenia, gdy nie jest częścią instrukcji. Przykładowo, wyrażenie może pojawić się po prawej stronie operatora przypisania lub jako cześć instrukcji SQL. Operatory tworzące wyrażenie wraz z typem ich operandów określają typ wyrażenia. Operand jest argumentem operatora. Operatory PL/SQL pobierają albo jeden argument (operator unarny), albo dwa argumenty (operator binarny). Np. operator nagacji (-) jest operatorem unarnym, natomiast operator mnożenia (*) operatorem binarnym. Powyżej w tabeli wymieniono operatory uporządkowane w zależności od posiadanego priorytetu (od najwyższego do najniższego). Wyrażenia znakowe Znakowy operator, którym jest operator konkatenacji ( ), łączy dwa ciągi znaków alfanumerycznych (lub argumentów, które mogą być niejawnie konwertowane do ciągu znaków). Jeżeli wszystkie operandy są w wyrażeniu konkatenacji typu CHAR, to wynik wyrażenia jest typu CHAR. Gdy chociaż jeden z operandów jest typu VARCHAR2, wówczas wynik wyrażenia jest typu VARCHAR2 Wyrażenia logiczne Wszystkie struktury sterowania PL/SQL (z wyjątkiem GOTO) zawierają wyrażenia logiczne (boolowskie), zwane również warunkami. Wyrażeniem logicznym jest każde wyrażenie, którego wynikiem jest wartość logiczna (TRUE, FALSE lub NULL). Trzy operatory logiczne iloczyn logiczny AND, suma logiczna OR oraz negacja logiczna NOT pobierają argumenty logiczne i zwracają wartości logiczne. Operatory te działają na zasadach standardowej, trójwartościowej logiki. NOT TRUE FALSE NULL FALSE TRUE NULL AND TRUE FALSE NULL TRUE TRUE FALSE NULL FALSE FALSE FALSE FALSE NULL NULL FALSE NULL OR TRUE FALSE NULL TRUE TRUE TRUE TRUE FALSE TRUE FALSE NULL NULL TRUE NULL NULL Oczywiście operatory logiczne można ze sobą łączyć, np. z_z1 AND z_z2 OR z_z3 AND NOT z_z4, przy czym należy podkreślić, iż zmienne PL/SQL z_z1, z_z2, z_z3 i z_z4 są zadeklarowane 22
jako zmienne typu BOOLEAN. Choć istnieją ścisłe reguły kolejności wykonywania działań (najpierw NOT, potem AND i w końcu OR), za pomocą których PL/SQL oblicza rezultat, jednak znacznie lepszym rozwiązaniem jest używanie nawiasów, dzięki którym wyrażenie staje się bardzie czytelne: (z_z1 AND z_z2) OR (z_z3 AND (NOT z_z4)). Ponieważ w PL/SQL obowiązuje logika trójwartościowa (oprócz TRUE i FALSE mamy jeszcze wartość nieokreśloną NULL), a zatem sprawdzenie dość skomplikowanego wyrażenia logicznego można przysporzyć niekiedy kłopotów i dlatego lepiej zapisywać je w sposób, który nie budzi sprzeczności, co do kolejności wykonywania operacji logicznych. Złożone typy danych Do tej pory zostały omówione najprostsze i najczęściej używane typy danych tzw. typy skalarne (NUMBER, VARCHAR2, itd.), które są predefiniowane w pakiecie STANDARD. Tym niemniej PL/SQL oferuje wiele innych typów danych, w tym także typy danych definiowane przez użytkownika. Przed zastosowaniem złożonego typu w bloku programu konieczna jest oczywiście jego deklaracja, a następnie zadeklarowanie zmiennych tego typu. Wśród złożonych typów danych (ang. composite datatype) oferowanych przez PL/SQL wyróżniamy: rekord (ang. record) jest złożonym typem danych (ang. composite datatype) co oznacza, iż pojedynczy rekord może zawierać wiele elementów informacji różnego typu kolekcja (ang. collection) jest to jednowymiarowa tablica w wersji specyficznej dla PL/SQL. Kolekcja służy do przechowywania list danych i w wielu przypadkach jej zastosowanie zwiększa wydajność aplikacji. obiekt (ang. object) jest to struktura podobna do rekordu. Jako typ złożony zawiera zarówno atrybuty (zmienne innych typów), jak i metody (podprogramy). Użycie typu obiektowego pozwala wykorzystać dodatkowe możliwości programowania, jakie oferuje programowanie zorientowane obiektowo. Oczywiście można by sobie wyobrazić programowanie w języku PL/SQL bez stosowania tych złożonych typów danych, czego nie dałoby się już powiedzieć o skalarnych typach danych, które są w tym przypadku praktycznie nieodzowne. Tym niemniej stosowanie w programie języka PL/SQL typów danych definiowanych przez użytkownika, zwiększa jego czytelność i jest samo w sobie bardziej interesującą pracą. Poniżej omówimy rekordowy typ danych, natomiast kolekcje i obiekty zostaną omówione nieco później podczas analizy zagadnień związanych z programowaniem w języku PL/SQL. 23
Rekordy w języku PL/SQL Rekord jest złożonym typem danych i składa z pojedynczych, logicznie ze sobą powiązanych elementów, które zwane są polami (ang. fields). Z każdym polem jest skojarzona pewna wartość i jest on dostępny oddzielnie w ramach rekordu. Każde pole w rekordzie ma swoją nazwę i jest najczęściej skalarnego typu. Tym niemniej polem w rekordzie może być również np. rekord. Wykorzystanie typów rekordowych pozwala abstrakcyjnie powiązać ze sobą dane w jedną całość, ponieważ pracujemy z grupą danych, a nie z poszczególnymi elementami. Na ogół, taki zabieg pozwala na uproszczenie kodu programu, dzięki czemu staje się on bardziej efektywny i zrozumiały. Rekordy języka PL/SQL są podobne do struktur języka C. Jak z powyższego wynika, rekordowe typy danych mają formę struktury. Struktura to zmienna złożona zawierająca listę zmiennych, które zwykle mają określone nazwy i różne typy danych. Typ rekordowy można zdefiniować: jawnie, niejawnie. Ponieważ rekord jest złożonym typem danych, to w przeciwieństwie do zmiennych skalarnych, które są definiowane jawnie poprzez jawną deklarację zmiennych poszczególnych typów, zmienną typu rekordowego definiujemy najpierw określając sam typ rekordowy, a dopiero następnie deklarujemy zmienną tego typu. Jawne definiowanie rekordu (typu rekordowego) A zatem ogólna postać składni definicji typu rekordowego jest następująca: TYPE nazwa_typu_rekord IS RECORD ( nazwa_pola1 TYP_DANYCH [NOT NULL] [DEFAULT := wyrazenie1], nazwa_pola2 TYP_DANYCH [NOT NULL] [DEFAULT := wyrazenie2],... nazwa_pola TYP_DANYCH [NOT NULL] [DEFAULT := wyrazenien] ); gdzie: nazwa_typu_rekord jest nazwą nowego typu, który określa strukturę nowego rekordu zawierającego N pojedynczych pól; nazwa_pola1... nazwa_polan są nazwami pól wewnątrz rekordu. Każde pole może być innego typu może to być typ skalarny, typ użytkownika lub inny typ rekordowy; więzy NOT NULL i klauzula DEFAULT (lub operator przypisania :=) z początkowymi wartościami wyrazenie1...wyrazenien są opcjonalne. A zatem wyrazenie1...wyrazenien są w tym przypadku wyrażeniami, których wartość jest wyznaczana zgodnie z tym samym typem danych, jakie jest zdefiniowane dla danego pola. 24
Rekord może zawierać dowolną liczbę pól. Deklarowanie zmiennej rekordowej Po zdefiniowaniu typu rekordowego o określonej strukturze, można zadeklarować rekordy odpowiadające temu zdefiniowanemu typowi. Składania deklaracji zmiennej typu rekordowego nie różni się od deklaracji zmiennych typów skalarnych, a zatem jej postać jest następująca: z_zmienna nazwa_typu_rekord; gdzie: z_zmienna jest arbitralną nazwą deklarowanej zmiennej typu rekordowego; nazwa_typu_rekord jest nazwą typu rekordowego. Uwaga: Definicja TYPE... RECORD jest pewną abstrakcją i z tego powodu nie może być używana samodzielnie. Zdefiniowanie deklaracji TYPE... RECORD nie zajmuje miejsca w pamięci, dopóki nie zostaną zadeklarowane zmienne tego typu. Używanie typu rekordowego Dopiero po zdefiniowaniu typu rekordowego i zadeklarowaniu zmiennych tego typu można wykorzystywać ten rekord do przetwarzania danych. W tym celu należy wykonać następujące czynności: uzyskać dostęp do poszczególnych elementów rekordu, zapamiętać dane w rekordzie, wykonać operacje porównania na rekordzie w celu stwierdzenia równości. Rozważmy następujący przykład deklaracji informacji związanych z danymi o produkcie: z_id z_nazwa z_cena z_wycofany NUMBER; VARCHAR2(30); NUMBER(7,2); CHAR(3); Wszystkie z powyższych trzech zmiennych są ze sobą logicznie powiązane, ponieważ odwołują się do wspólnych pól w tabeli bazy danych towary. A zatem są ze sobą w pewien abstrakcyjny sposób powiązane, co umożliwia ich połączenie w jedną całość za pomocą odpowiednio zdefiniowanego rekordu. /* Zdefiniowanie typu rekordu do przechowywania wspólnych danych o produkcie */ TYPE t_rekordprodukt IS RECORD ( z_id NUMBER, z_nazwa VARCHAR2(30), 25
z_cena z_wycofany ); NUMBER(7,2), CHAR(3) /* Zadeklarowanie zmiennej zdefiniowanego powyżej typu rekordowego t_rekordprodukt*/ z_daneproduktu t_rekordprodukt ; Po zadeklarowaniu dla tych zmiennych typu rekordu relacja między nimi staje się bardziej widoczna i można nimi manipulować, tak jakby tworzyły one pewną całość. Uzyskiwanie dostępu do poszczególnych pól rekordu W celu odwołania do pola wewnątrz rekordu jest stosowana notacja kropkowa, którego składnia jest następująca: nazwa_rekordu.nazwa_pola gdzie: nazwa_rekordu jest nazwą rekordu. Należy wyraźnie zaznaczyć, iż jest to nazwa zmiennej typu rekordowego, a nie nazwa samego typu rekordowego; nazwa_pola jest nazwą pola w ramach tego rekordu, do którego chcemy uzyskać dostęp. Poniższy przykład przedstawia sposób odwołania do pola w rekordach z powyżej podanego przykładu dotyczącego zdefiniowania rekordu t_rekordprodukt: BEGIN z_daneproduktu.z_id := 20; z_daneproduktu.z_nazwa:= Napój jabłkowy ; END; Jak nietrudno zauważyć, składnia dostępu do pola w rekordzie przypomina składnię używaną podczas wykorzystywania kolumny w tabeli bazy danych. W ten sposób jasnym staje się analogia pomiędzy tabelami bazodanowymi a rekordami. Tym niemniej różnią się one tym, iż pierwsze są składowane w bazie danych, natomiast drugie nie mogą być i nie są w niej składowane. Uwaga: Kiedy korzystamy z poszczególnych pól rekordu, należy używać nazwy zmiennej typu rekordowego, a nie nazwy tego typu. Typy rekordowe są ściśle związane z językiem PL/SQL i jako takie nie są dostępne w SQL. Poza tym, nie mogą być składowane w tabelach bazy danych. Zmienna typu rekordowego PL/SQL jest zmienną, która może być wykorzystana zarówno do odczytu, jak i zapisu danych. W ten sposób możemy jej używać po obu stronach operatora przypisania. Własność ta została zilustrowana w poniższym przykładowym bloku programu: TYPE t_rekordprodukt IS RECORD 26