background image

 

 

Wyk

ład 

7

background image

 

 

Język PL/SQL

Część I

 

background image

 

 

Rozszerzenie języka SQL o 

elementy języka programowania

•  SQL/PSM - Standard SQL:1999
• Oracle PL/SQL

background image

 

 

Język PL/SQL

    Język 

PL/SQL

 jest proceduralnym 

rozszerzeniem języka SQL stanowi podstawę do 
pisania aplikacji Oracle – jest dostępny w 
różnych programach narzędziowych ORACLE 
np. 

• SQL*Plus, 
• Developer 2000 (Oracle*Forms i 

Oracle*Reports), 

• prekompilatory, 
• procedury, 
• wyzwalacze, 
• aplikacje na stronach WWW.

background image

 

 

DECLARE

       deklaracje obiektów PL/SQL jak zmienne, 

stałe, wyjątki,   procedury, funkcje

  

BEGIN

       ciąg instrukcji do wykonania
  

EXCEPTION

       obsługa wyjątków (błędów)

END

;

• Deklaracje i obsługa wyjątków są opcjonalne. 

Bloki mogą być zagnieżdżone. 

• Instrukcje SQL w bloku PL/SQL: SELECT, 

INSERT, UPDATE, DELETE, COMMIT i 
ROLLBACK.

Blok anonimowy

background image

 

 

 CREATE TABLE Magazyn(Produkt 

VARCHAR2(30), Stan NUMBER(4));

CREATE TABLE Zakupy(Info VARCHAR2(100), 

Data DATE);

CREATE TABLE Dziennik(Rejestr 

VARCHAR2(100));

 INSERT INTO Magazyn VALUES('Fiat', 1);
 INSERT INTO Magazyn VALUES(‘Honda', 3);
 INSERT INTO Magazyn VALUES(‘Toyota', 2);

Przykładowe tabele

background image

 

 

DECLARE
   v_stan NUMBER(5);

BEGIN

   SELECT Stan INTO v_stan FROM 
Magazyn
      WHERE Produkt = 'Fiat';

 

   IF v_stan > 0  THEN
        UPDATE Magazyn SET Stan = Stan 
- 1
        WHERE Produkt = 'Fiat';
        INSERT INTO Zakupy
        VALUES ('Kupiono Fiata', 
Sysdate);
  ELSE
       INSERT INTO Zakupy
       VALUES ('Brak Fiatów', 
Sysdate);
  END IF;
  COMMIT;
EXCEPTION 
  WHEN  no_data_found  THEN        
     INSERT INTO dziennik
     VALUES('Nie znaleziono FIATa');
END;

Przykład

Tylko 

jeden

 rekord!!!

background image

 

 

• między nawiasami /*    i    */ , albo

• od dwóch kresek -- do końca bieżącej linii.

Komentarze

background image

 

 

v_zarobki NUMBER(7,2);

pi CONSTANT NUMBER(7,5) := 3.14159;

v_nazwisko VARCHAR2(25) := 'Kowalski';

v_data DATE := Sysdate;

żonaty BOOLEAN := False;

liczba_dzieci BINARY_INTEGER :=0;

Deklaracji zmiennych tych samych typów nie 
można 
łączyć razem jak w innych językach!

Deklaracje zmiennych i stałych

background image

 

 

W PL/SQL są dostępne typy danych z języka SQL a 

ponadto m.in.

• BOOLEAN

 - wartości logiczne,  

• BINARY_INTEGER

 - typ liczb całkowitych – 

niezależny od podtypów typu NUMBER i przez to 
wymagający przy zapisie mniej miejsca w pamięci 
RAM. 

• Nie należy zmiennej nadawać tej samej nazwy co 

kolumnie w tabeli.

• Wewnątrz bloku PL/SQL instrukcja SELECT nie 

wypisuje wyników na ekran (ani do pliku).

• Zmienne i stałe PL/SQL mogą występować w 

instrukcjach SQL.

 

Deklaracje zmiennych i stałych

background image

 

 

Zmienne w SQL*Plus

• VARIABLE X NUMBER

 (lub 

CHAR(n)

 lub 

VARCHAR2(n)

, (n>0) – deklaracja zmiennej 

wiązania;

• EXECUTE :X := wyrażenie;

 – przypisz na zmienną 

wiązania 

:X

 wartość wyrażenia. Po ustaleniu 

wartości można zmienne wiązania poprzedzone 
dwukropkiem używać w instrukcjach SQL i PL/SQL.

• PRINT X

 – wypisz wartość zmiennej wiązania X;

• ACCEPT Zmienna PROMPT 'Podaj wartość 

zmiennej: '

 – utworzenie zmiennej podstawienia i 

wczytanie na nią wartości. Następnie można 
zmienne podstawienia poprzedzone znakiem & 
używać w instrukcjach SQL i PL/SQL.

background image

 

 

SET ServerOutput ON

ACCEPT rocz_zarob PROMPT 'Podaj roczne 
zarobki: '

DECLARE

     mies

 NUMBER(9,2) := &rocz_zarob;

BEGIN

     mies := mies/12;

    DBMS_OUTPUT.PUT_LINE ('Miesięczne zarobki 
=  ' ||mies);

END

;

Przykład

background image

 

 

ACCEPT rocz_zarob PROMPT 'Podaj roczne 
zarobki: '

VARIABLE mies NUMBER

BEGIN

  :mies := &rocz_zarob/12;

END

;

/

PRINT Mies

Przykład

background image

 

 

Zmienne systemowe

• SQL%ROWCOUNT

 -  liczba wierszy 

przetworzonych przez ostatnią instrukcję SQL;

• SQL%FOUND

 = TRUE jeśli został znaleziony 

(przetworzony) przynajmniej jeden wiersz;

• SQL%NOTFOUND

 = TRUE jeśli żaden wiersz nie 

został znaleziony (przetworzony);

• SQLERRM

 - tekstowa informacja o błędzie;

• SQLCODE

 - kod błędu. 

     Obie zmienne SQLERRM i SQLCODE można 

używać tylko w sekcji EXCEPTION!

background image

 

 

Przykład użycia zmiennej SQL%ROWCOUNT 
do obliczenia liczby usuwanych działów o 
numerze 50:

 

DECLARE

 usunięte NUMBER;

 

BEGIN

         DELETE FROM Dept WHERE Deptno 
= 50;
         usunięte := SQL%ROWCOUNT;
         INSERT INTO DziennikDzialow
         VALUES ('Dział 50', usunięte, 
Sysdate);
 

END

;

Przykład

background image

 

 

Instrukcje warunkowe

IF

  warunek  

THEN

  

     ciąg_instrukcji  

END

 

IF

;

IF

  warunek  

THEN

  

     ciąg_instrukcji

ELSE

  

     ciąg_instrukcji  

END IF

;

 

IF

  warunek  

THEN

  

     ciąg_instrukcji

ELSIF

  warunek  

THEN

 

     ciąg_instrukcji  

END IF

• Instrukcje po THEN 

są wykonywane 
wtedy, gdy wartością 
warunku jest TRUE. 

• Instrukcje po ELSE 

są wykonywane 
wtedy, gdy wartością 
warunku jest FALSE 
lub NULL.

 

background image

 

 

Instrukcje iteracji

LOOP

    

ciąg

 

instrukcji

 

(w tym  EXIT 

lub  EXIT  WHEN  warunek)

END LOOP

;

 

FOR

  

zmienna

 

IN

  

wartość1 ..  

wartość2

LOOP

 

 …

  

END LOOP

;

 

WHILE

  

warunek

LOOP

 … 

END

 

LOOP

;

background image

 

 

Instrukcja pusta

Null

 

– na przykład, gdy obsługa wyjątku 

jest pusta.

background image

 

 

Zmienne wierszowe

DECLARE

     rek_osoby Emp%ROWTYPE;     /* Typ 

wierszowy */

BEGIN

    SELECT * INTO rek_osoby 
      FROM Emp WHERE Ename = 'KOWALSKI';
    rek_osoby.Sal := 1.1*rek_osoby.Sal;
    INSERT INTO Dziennik 
      VALUES (rek_osoby.Ename, rek_osoby.Job, 
                        rek_osoby.Sal, SYSDATE);

END

;

    Zmiennej wierszowej nie można użyć 

bezpośrednio po słowie kluczowym VALUES w 
instrukcji INSERT INTO!

background image

 

 

Rekordy PL/SQL, zmienne 

rekordowe

TYPE

 Typ_rek_prac 

IS RECORD

    ( numer_prac NUMBER(4) NOT NULL,
      nazwisko     VARCHAR2(40) NOT NULL,
      zarobki        NUMBER(8,2),

        num_działu NUMBER(4));

rekord_prac Typ_rek_prac;

    Typy rekordowe mogą być zagnieżdżone. Dostęp 

do pól rekordu jest przy pomocy notacji 
kropkowej. Na zmienną rekordową można 
przypisać wartość innej zmiennej rekordowej, ale 
tylko tego samego typu rekordowego. Można ich 
używać w klauzuli INTO (tak jak zmiennych 
wierszowych):

                       SELECT *   INTO rekord_prac
                       FROM Pracownicy
                       WHERE Id_prac = 12;

background image

 

 

Instrukcja SELECT w PL/SQL

Aby instrukcja była poprawna, instrukcja SELECT 
… INTO
 musi zwracać tylko jeden wiersz wyników!

    SELECT Ename
    INTO v_ename
    FROM Emp
    WHERE Empno = 1030;

Wartości w bloku PL/SQL, pochodzą na ogół z bazy 
danych, gdzie został określony ich typ danych. W 
związku z tym, wygodnie jest określać typ danych 
jako ''typ danych wymienionej kolumny'' np. zamiast

 

DECLARE

 

v_ename VARCHAR2(30)

        

DECLARE

 v_ename  Emp.Ename%TYPE

background image

 

 

Instrukcja SELECT w PL/SQL

Możemy natomiast odebrać cały wiersz 
(jeden!), a nie tylko pojedynczą kolumnę:

    SELECT *
    INTO v_pracownik
    FROM Emp
    WHERE Empno = 1030;

Zmienną v_pracownik (która jest wierszem) 
deklarujemy  w ten sposób:

        

DECLARE

 v_pracownik  Emp

%ROWTYPE

background image

 

 

Kursory: dostęp do obszarów 

roboczych instrukcji SELECT

•  Kursor – bufor, do którego są zapisywane,  

kolejno sprowadzane z bazy danych, wiersze 
z wynikami zapytania.

 

• Definicja kursora (przyporządkowanie 

instrukcji SELECT)

CURSOR nazwa_kursora IS
instrukcja_SELECT; 

– (bez INTO!)

• Otwarcie kursora (wykonanie instrukcji 

SELECT)

OPEN nazwa_kursora;

background image

 

 

Kursory

• Pobieranie kolejnych wierszy

 

        FETCH  nazwa_kursora INTO 

zmienna, …;

• Wyjście z pętli po sprowadzeniu wszystkich 

wierszy

 

   EXIT  WHEN nazwa_kursora
%NOTFOUND;

• Zamknięcie kursora

 

        CLOSE nazwa_kursora;

background image

 

 

CREATE OR REPLACE PROCEDURE

 

SumujZarobki 

AS

       zarobki REAL:=0;

       

CURSOR

 kursor_osoba 

IS

                    SELECT * FROM Emp; 
        rek_osoby kursor_osoba%ROWTYPE;

BEGIN

 

OPEN

 kursor_osoba;

      

LOOP

           

FETCH

 kursor_osoba INTO rek_osoby;

           EXIT WHEN kursor_osoba%NOTFOUND;

           zarobki := zarobki + rek_osoby.Sal;

      

END LOOP;

      Dbms_output.Put_line('W sumie zarobki = '||
zarobki);

     

CLOSE

 kursor_osoba;

END

;

Przykład

Nasz bufor

background image

 

 

Kursor – aliasy 

kolumn

      Na liście SELECT mogą się znajdować dowolne 

wyrażenia, przy czym jeśli nie jest to nazwa 
kolumny, musi być użyty alias np.,

 

 

      

CURSOR

 kursor 

IS

 

           SELECT Ename, Sal+NVL(Comm,0) AS 

Uposażenie              

           FROM Emp;

 Iteracja - wersja z kursorem:

     

FOR

 rek_osoby 

IN

 kursor_osoba 

     

LOOP

        ...  

     

END LOOP

   – 

OPEN

FETCH

 i 

CLOSE

 są tu 

niejawne

!

background image

 

 

Kursor z 

parametrami

     W instrukcji SELECT w kursorze mogą 

występować parametry. Ten sam kursor może 
być otwarty wielokrotnie – z różnymi 
wartościami parametrów.

 

       CURSOR nazwa_kursora(parametr 

typ_danych, ....) IS

                    instrukcja-SELECT;

DECLARE

  

CURSOR

 emp_cursor (v_deptno NUMBER, 

v_job VARCHAR2) 

IS

    SELECT

Empno, Ename

       FROM

Emp

       WHERE Deptno = v_deptno AND Job = 

v_job;

BEGIN

  

OPEN

 emp_cursor(10, 'CLERK');

...

background image

 

 

Atrybuty kursora

•   kursor%FOUND -- czy z bazy danych 

sprowadzono kolejny wiersz,

•   kursor%NOTFOUND -- czy koniec 

sprowadzania wierszy,

•   kursor%ROWCOUNT -- liczba 

sprowadzonych dotąd wierszy,

•   kursor%ISOPEN -- czy kursor jest otwarty.

 

 

IF

 NOT prac_kursor%ISOPEN 

THEN

   

OPEN

 prac_kursor;

   

END IF

;

   

LOOP

     

FETCH

 prac_kursor...

background image

 

 

Aktualizacja wierszy za pomocą 

kursora

• Przy wykonywaniu instrukcji SELECT można 

zakładać blokady na wiersze w celu ich 
modyfikacji.

 

        FOR UPDATE [OF kolumna, kolumna, 

… ]

     Podane kolumny określają tabele, których 

wiersze mają zostać zablokowane.

• Stowarzyszona z nią w instrukcji UPDATE lub 

DELETE klauzula

 

        WHERE CURRENT OF kursor

 

     umożliwia modyfikację lub usunięcie 

sprowadzonego przez kursor wiersza 
odpowiedniej tabeli.

 

background image

 

 

DECLARE

    

CURSOR

 kursor_osoba  

IS

         SELECT Ename, Sal FROM Emp 
         

FOR UPDATE OF

 Sal;

    rek_osoby kursor_osoba%ROWTYPE;

BEGIN

    

OPEN

 kursor_osoba;

    

LOOP

         

FETCH

 kursor_osoba 

INTO

 rek_osoby;

         

EXIT WHEN

 kursor_osoba%NOTFOUND;

         

IF

 rek_osoby.Sal < 10000  

THEN

                UPDATE Emp SET Sal = Sal * 1.1
                

WHERE CURRENT OF

 kursor_osoba;

         

END IF

;     

                /* zamiast modyfikować możemy też 
usunąć wiersz np. 
                DELETE Emp WHERE CURRENT OF 
kursor_osoba; */
   

END LOOP

;

   

CLOSE

 kursor_osoba;

   COMMIT;

END

;

Przykład

background image

 

 

 Standardowe, nazwane wyjątki

 

• dup_val_on_index

 (ta sama wartość w 

indeksie jednoznacznym), 

• no_data_found

 (instrukcja SELECT nie 

zwróciła wartości dla zmiennych w klauzuli 
INTO), 

• too_many_rows

 (instrukcja SELECT zwróciła 

więcej niż jeden wiersz wartości dla 
zmiennych w klauzuli INTO),

• zero_divide

 (dzielenie przez zero),

• timeout_on_resource

 (zbyt długie 

oczekiwanie na zasoby),

• invalid_cursor

 (niepoprawna operacja na 

kursorze),

• invalid_number

 (niepoprawna konwersja na 

liczbę).

 

background image

 

 

Wyjątki

 

…..  SELECT Ename, Job   INTO v_ename, v_job  …..
EXCEPTION

   WHEN 

no_data_found

  THEN

INSERT INTO Dziennik VALUES ('Nikt nie zatrudniony 
w 1993');
DBMS_OUTPUT.Put_line('Nikt nie zatrudniony w 93');

   WHEN 

too_many_rows

  THEN

INSERT INTO Dziennik VALUES ('Więcej niż 1 
zatrudniony w 1993');
DBMS_OUTPUT.Put_line('Więcej niż 1 w 93');

  WHEN 

OTHERS

  THEN     -- Obsługa pozostałych 

błędów 
komunikat := 'Błąd nr.=  ' ||
                              SQLCODE|| ', komunikat=  ' || 
Substr(SQLERRM,1,100);

               -- SQLCODE i SQLERRM nie mogą wystąpić w 

instrukcji SQL!

     

INSERT INTO Dziennik VALUES (komunikat);

     

DBMS_OUTPUT.Put_line(‘Wystąpił inny błąd ');

 END;

background image

 

 

Obsługa wyjątków

 

• Jeśli blok, w którym wystąpił błąd, zawiera 

obsługę tego błędu, to po dokonaniu obsługi, 
sterowanie jest w zwykły sposób 
przekazywane do bloku go zawierającego 
(nadrzędnego). 

• Jeśli nie zawiera, następuje przekazanie błędu 

do bloku zawierającego dany blok i albo tam 
nastąpi jego obsługa albo błąd przechodzi do 
środowiska zewnętrznego.

background image

 

 

Obsługa wyjątków

 

• Błąd, który wystąpił w sekcji wykonawczej bloku 

(między BEGIN i END) jest obsługiwany w sekcji 
EXCEPTION tego samego bloku. Błędy, które 
wystąpią w sekcji deklaracji lub w sekcji wyjątków 
są przekazywane do bloku zawierającego dany 
blok.

• Dobra praktyka programistyczna wymaga aby 

każdy błąd został obsłużony – ewentualnie w 
klauzuli WHEN OTHERS THEN najbardziej 
zewnętrznego bloku. 

• Aby móc stwierdzić, która instrukcja SQL 

spowodowała błąd:

•   można używać podbloków z własną obsługą 
błędów, albo 

•    można używać licznika, zwiększającego się 
o jeden po wykonaniu każdej instrukcji SQL.

background image

 

 

Wyjątki definiowane przez 

programistę

Wyjątki można deklarować samemu (w sekcji 
DECLARE) używając słowa kluczowego 
EXCEPTION

nazwa_wyjątku EXCEPTION;

powodować ich podniesienie (w sekcji 
instrukcji)

 

RAISE nazwa_wyjątku;

a następnie je obsługiwać (w sekcji 
EXCEPTION)

WHEN nazwa_wyjątku THEN ...

background image

 

 

DECLARE

      brak_w_magazynie 

EXCEPTION

;

      v_stan NUMBER(5);

BEGIN

   SELECT Stan
      INTO v_stan
      FROM Magazyn WHERE Produkt = 
'Fiat';
   

IF

 v_stan < 1  

THEN

            RAISE brak_w_magazynie;
   

END IF

;

EXCEPTION

    

WHEN

 brak_w_magazynie  

THEN

          INSERT INTO Zamówienia VALUES 
('Fiaty');
          

RAISE

 wyjście;

END

;

Przykład

background image

 

 

Podnoszenie wyjątku za pomocą 

Raise_Application_Error

• Przypisanie mu numeru między –20000 a –20999 i 

tekstu. 

• Wyjątek taki może zostać obsłużony albo w tym samym 

bloku albo w aplikacji zewnętrznej, w której to 
wywołanie zostanie wykonane.

DECLARE
      numer INTEGER;
BEGIN     .....

       Raise_Application_Error(-20100,'Błąd');

EXCEPTION
  WHEN OTHERS THEN
         numer:=SQLCODE;
         IF numer= -20100 THEN           
               Dbms_output.Put_line('Błąd 

przechwycony!');

         END IF;
END;

background image

 

 

Obsługa błędów przechwytywanych 

przez serwer bazy danych

Nadanie nazwy wyjątkowi systemowemu - w sekcji 
deklaracji

 

 

nazwa_wyjątku EXCEPTION;

 

PRAGMA EXCEPTION_INIT (nazwa_wyjątku

numer_błędu);

 

a następnie obsługa (w sekcji EXCEPTION)

WHEN nazwa_wyjątku THEN ...

background image

 

 

Przykład obsługi błędu naruszenia więzów klucza obcego nr  
-2292:

DECLARE

       bl_klucz_o 

EXCEPTION

;

       

PRAGMA EXCEPTION_INIT

 (bl_klucz_o, -2292);

       v_deptno Dept.Deptno%TYPE := :b_deptno;

BEGIN

     DELETE FROM Dept
      WHERE Deptno = v_deptno;
    COMMIT;

EXCEPTION

    

WHEN

 bl_klucz_o 

THEN

          Dbms_output.Put_line ('Nie można usunąć działu ' 
||  
          To_Char(v_deptno) || ', w którym są pracownicy. ');

END

;

Przykład


Document Outline