piątek, 27 grudnia 2013

Klauzula RETURNING w PL/SQL

W PL/SQL istnieje opcjonalna klauzula RETURNING INTO pozwalająca na zapisanie do zmiennej wartości pochodzących z rekordu wstawianego przez polecenie INSERT lub modyfikowanego przez UPDATE. Taka możliwość staje się bardzo użyteczna, gdy zechcemy uzyskać ID pochodzącego z sekwencji właśnie wstawionego rekordu, lub generowanej dynamicznie innej wartości (np. jeśli wstawiamy sysdate).


W powyższym przykładzie wstawiłem nowy wiersz do tabeli jobs, a przy pomocy klauzuli RETURNING INTO uzyskałem ID wstawionego wiersza. Wartość ID została przypisana do zmiennej ID której typ został zdeklarowany na podstawie typu kolumny job_id z tabeli jobs. Taka możliwość nabiera ogromnego znaczenia, jeśli wartość wstawiana do kolumny klucza głównego pochodzi z sekwencji, a mamy zamiar operować na właśnie wstawionych danych. Oczywiście w takim przypadku można by również zastosować odwołanie do currval wykorzystywanej sekwencji, jednak nie mamy żadnej gwarancji, że w międzyczasie ktoś inny nie skorzystał z tej samej sekwencji i nie zmienił jej wartości.

sobota, 21 grudnia 2013

Klauzula FOR UPDATE

Przy użyciu klauzuli FOR UPDATE możemy zablokować wiersze do edycji przez inne sesje. Działa to na zasadzie transakcyjnej blokady zasobów. Jeśli my wykonamy jakiś UPDATE lub DELETE, wiersze których te polecenia zostaną zablokowane do czasu zatwierdzenia lub wycofania transakcji. W tym czasie inne sesje usiłujące dokonać jakiejkolwiek zmiany będą musiały oczekiwać na zwolnienie zasobów przez nas. Najniższy poziom blokady to wiersz, tak więc nawet jeśli zmienimy zawartość jednej kolumny, nikt nie będzie mógł zmienić również pozostałych kolumn w tych wierszach.
Klauzulę tę możemy wykorzystywać zarówno w SQL, jak i w kursorach w PL/SQL. Przykład użycia w SQL:






Wyświetlam 3 osoby z departamentu nr 90 , jednocześnie blokując te wiersze do edycji przez inne sesje. Teraz z innej sesji usiłuję te wiersze zmodyfikować:



Zauważ że modyfikuję inną kolumnę, niż te które wyświetlałem z pierwszej sesji. Sesja czeka na zwolnienie zasobów. Możemy teraz swobodnie dokonać zmian, bez obawy że ktoś inny w międzyczasie dokona jakichś zmian na „naszych” wierszach.


Dopiero po wydaniu polecenia „COMMIT”, wiersze zostają odblokowane i sesja która oczekiwała na odblokowanie zasobów może dokonać zmian:





Klauzulę FOR UPDATE możemy wykorzystywać również w PL/SQL w kursorach. Samo zadeklarowanie kursora nie spowoduje jednak blokady wierszy, jak się za chwilę przekonamy.
Uruchomiłem blok anonimowy z samą deklaracją kursora:


Aktualizacja z innej sesji przebiegła bez żadnych problemów:





Aby wiersze zostały zablokowane , kursor trzeba przynajmniej otworzyć:



Nie koniecznie musi to być otwarcie jawne, może być to również automatyczne otwarcie kursora które
następuje w pętli kursorowej, tak jak to widać poniżej:




Z wykorzystaniem klauzuli for update wiąże się również klauzula „WHERE CURRENT OF” która pozwala aktualizować lub kasować wiersze zablokowane przez kursor.



Istotna uwaga: klauzula WHERE CURRENT OF odnosi się do wiersza który właśnie został zfetchowany z kursora.

Niezależnie od ilości wierszy w kursorze, klauzula WHERE CURRENT OF odnosi się do ostatnio pobranego z kursora wiersza. Poniżej zastosowałem pętle kursorową, i jak widzimy zawsze ilość zaktualizowanych wierszy wynosi 1.






W przypadku próby wykorzystania klauzuli WHERE CURRENT OF bez uprzedniego fetcha, dostajemy błąd :





sobota, 14 grudnia 2013

Funkcje deterministyczne

Najpierw wyjaśnijmy, co to znaczy że funkcja jest lub nie jest deterministyczna. Funkcja jest deterministyczna wtedy, kiedy dla takich samych parametrów zwróci zawsze ten sam wynik. To oznacza, że taka funkcja musi działać zawsze w ten sam sposób, a na wynik nie powinny wpływać żadne czynniki zewnętrzne tj. funkcja nie powinna korzystać z żadnych zmiennych pakietowych ani innych źródeł zewnętrznych. Taka funkcja nie może też zmieniać żadnych danych w bazie (w tabelach ani pakietach). W niektórych sytuacjach wymagane jest by funkcja była deterministyczna. Przykładowo jeśli zechcemy użyć własnej funkcji w indeksie funkcyjnym, to funkcja ta musi być deterministyczna. Nie tylko spełniać warunek jako taki, ale też musi to być jasno określone w treści funkcji.
Poniżej przykład. Tworzę zwykłą funkcję, której już konstrukcja jasno wskazuje że funkcja jest deterministyczna (wartość parametru zawsze zostanie podzielona przez 12 i zaokrąglona do 2 miejsca po przecinku). Nie jest to jednak określone specjalną klauzulą DETERMINISTIC.
Przy próbie wykorzystania takiej funkcji w indeksie funkcyjnym dostajemy błąd
„ ORA-30553 The function is not deterministic”






Teraz dodaję klauzulę DETERMINISTIC (ponadto nic się nie zmienia), i przebudowuję funkcję, a następnie ponownie próbuję stworzyć indeks funkcyjny w oparciu o tę funkcję:



Tym razem obyło się bez problemów.

sobota, 7 grudnia 2013

Pakiet DBMS_SQL

Pakiet DBMS_SQL możemy wykorzystywać alternatywnie do klauzuli EXECUTE IMMEDIATE. Każdą z czynności : otwarcie kursora, parsowanie zapytania, wykonanie zapytania i zamknięcie kursora wykonujemy tutaj ręcznie i osobno. To ma swoje zady i walety. Z jednej strony jest więcej pisania i ilość kodu gwałtownie wzrasta. Z drugiej strony mamy większą kontrolę nad wszystkim co się dzieje, kod jest nieco bardziej przejrzysty. Przeanalizujmy teraz porównianie obu tych technik.
Użycie EXECUTE IMMEDIATE :




To samo z użyciem pakietu DBMS_SQL:





W obu przypadkach musiałem konkatenować zapytanie, z tym że w pierwszym wykonanie całości sprowadza się do polecenia EXECUTE IMMEDIATE. W drugim mamy kilka innych poleceń, wywołań procedur i funkcji z pakietu DBMS_SQL. Przeanalizujmy je po kolei.



dbms_sql.open_cursor
To funkcja alokująca przestrzeń na przetwarzanie kursora. Zwraca numeryczą referencję do kursora (taki identyfikator, dzięki któremu wiadomo o który kursor chodzi).



Dbms_sql.parse
Procedura ta sprawdza poprawność semantyczną zapytania (sprawdza czy wpisałeś zapytanie czy np. „Suchą szosą szedł sobie Sasza” ). W przypadku operacji DDL procedura ta wykonuje polecenie! W takim przypadku dzieje się to już tutaj i polecenie execute nie jest obowiązkowe.
Pierwszy parametr to identyfikator kursora (uzyskany przy wywołaniu funkcji open_cursor), drugi to zapytanie które będzie wykonywane, trzeci to wersja SQL. Można w trzecim parametrze ustawić zgodność SQL z wersją np. 6.












dbms_sql.execute
Funkcja wykonuje zapytanie. Jest zbędna w przypadku operacji DDL. W przypadku operacji DML zwraca ilość wierszy które uległy zmianie. W przypadku klauzuli EXECUTE IMMEDIATE również moglibyśmy się dowiedzieć ilu wierszy dotyczyła zmiana, ale musielibysmy dodatkowo użyć klauzuli SQL%ROWCOUNT.



dbms_sql.close
Dealokuje przestrzeń w pamięci operacyjnej wykorzystywaną na potrzeby przetwarzania kursora.



Możemy też wykorzystać zmienne bindowane w obu przypadkach. Wersja z EXECUTE IMMEDIATE:







Wersja z DBMS_SQL:



declare
dep integer:=90;
mana integer:=100;
podwyzka number:=400;
cu_id integer;
ile integer;
begin
cu_id:=dbms_sql.open_cursor;
dbms_sql.parse(cu_id, 'update employees set salary=salary+:p where department_id=:d and manager_id=:m',dbms_sql.native);
dbms_sql.bind_variable(cu_id,':p',podwyzka);
dbms_sql.bind_variable(cu_id,':d',dep);
dbms_sql.bind_variable(cu_id,':m',mana);
ile:=dbms_sql.execute(cu_id);
dbms_sql.close_cursor(cu_id);

end;






To co się tutaj pojawiło nowego, to wywołanie procedury bind_variable. Służy ona do podawania wartości jakie mają trafić do zmiennych bindowanych. Pierwszy parametr to identyfikator kursora, drugi zmienna bindowana użyta w zapytaniu, trzeci wartość do wstawienia do tej zmiennej. W zasadzie w obu przypadkach zamiast zmiennych dep,mana i podwyżka mógłbym użyć równie dobrze wartości bezpośrednio.

niedziela, 1 grudnia 2013

Wyrażenia regularne w Oracle (REGEXP)

Wyrażenia regularne

Funkcje

Wyrażenia regularne pozwalają nam wynajdywać w tekście fragmenty wg wzorców. Związane z regexp jest 5 funkcji:


REGEXP_COUNT

funkcja dostępna od wersji 11g. Zwraca ilość wystąpień elementów pasujących do wzorca. Poniższy przykład zwraca ilość liczb występujących w tekście:



select regexp_count('fff4ff563fffff','[[:digit:]]')
from dual;

















REGEXP_REPLACE




Funkcja zamienia elementy pasujące do wzorca na podany zamiennik. Na poniższym przykładzie widzimy zamianę wszystkich cyfr na ciąg XXX.



select regexp_replace('fff4ff563fffff','[[:digit:]]','XXX')

from dual;
















REGEXP_SUBSTR

Funkcja zwraca z podanego tekstu pierwszy element pasujący do wzorca. Poniżej widzimy wycięcie pierwszego przynajmniej 3 elementowego zbioru cyfr z tekstu:


select regexp_substr('eeeeee23ee44445eee444445','[[:digit:]]{3,}')

from dual;

















REGEXP_INSTR

Funkcja zwraca pozycję ciągu tekstowego pasującego do wzorca. Poniżej określenie pozycji elementu składającego się z przynajmniej 4 cyfr:



select regexp_instr('eeeeee23ee44445eee444445','[[:digit:]]{4,}')

from dual;


















REGEXP_LIKE

Służy sprawdzaniu czy podany tekst pasuje do wzorca:



select * from employees where
regexp_like(phone_number,'[[:digit:]]{3}\.[[:digit:]]{2}\.[[:digit:]]{4}\.[[:digit:]]{6}');









Wzorce w wyrażeniach regularnych




.
Dowolny znak
+
Jeden lub więcej wystąpień
?
Zero lub jedno wystąpienie
*
Dowolna ilość wystąpień (w tym zero)
{3}
Dokładnie trzy wystąpienia
{3,}
Przynajmniej trzy wystąpienia
{3,6}
Od trzech do sześciu wystąpień
|
Lub
[[:digit:]]
Cyfra
[[:alpha:]]
Litera
[[:alnum:]]
Znak alfanumeryczny
\
Wyłączenie znaku jako specjalnego. Np. jeśli zechcemy użyć kropki jako w kontekście znaku tekstowego, a nie znaku specjalnego oznaczającego dowolny znak






To oczywiście nie wszystkie możliwe wzorce, jest ich znacznie więcej. Wzorce dotyczące ilości wystąpień dotyczą zawsze poprzedzającego elementu. Kilka przykładów:



'[[:digit:]]{4,}'
Przynajmniej 4 cyfry
([[:alnum:]]| |\.|-){8,14}
Ciąg składający się z od 8 do 14 elementów wśród których mogą wystąpić dowolne litery i cyfry, spacje, kropki lub myślniki
[[:alnum:]]{1,}(\.[[:alnum:]]{1,})?@[[:alnum:]]{1,}\.[[:alpha:]]{2,4}
Przynajmniej jednoelementowy wyraz składający się z liter lub cyfr, po którym może nastąpić ciąg rozpoczynający się od kropki i składający się z przynajmniej jednej litery lub cyfry, po których nastąpi znak @, po których nastąpi przynajmniej jednoelementowy ciąg alfanumeryczny, po którym nastąpi kropka i ciąg składający się z samych tylko liter o całkowitej długości od 2 do 4 znaków. Czyli znajdziemy element wyglądający jak adres email :)