Poniżej jest kolejny przykład wykorzystania klauzuli model w wersji iteracyjnej, tym razem do wygenerowania kalendarza z uwzględnieniem świąt i dni roboczych na rok 2016. Jest zdefiniowany jeden wymiar oraz cztery miary: data, nazwa dnia tygodnia, nazwa święta obchodzonego w danym dniu oraz informacja, czy dany dzień jest roboczy.
Dodatkowo został przedstawiony sposób tworzenia funkcji nieskładowanych definiowanych jedynie na potrzeby SQL. To jest bardzo wygodne rozwiązanie, bo upraszcza składnię zapytań, zamiast komplikować składnię zapytania, niektóre fragmenty czytelniej jest przenieść do funkcji - np. kiedy byśmy chcieli napisać ogólny algorytm obliczania świąt ruchomych, to moim zdaniem lepiej umieścić go w procedurze Fdb_JestSwieto.
Przy pisaniu zapytania położyłem większy nacisk na czytelność niż wydajność..
WITH FUNCTION Fdb_JestSwieto(pd_dt DATE) RETURN VARCHAR2 AS
pn_Month NUMBER;
pn_Day NUMBER;
BEGIN
pn_Day := EXTRACT (DAY FROM pd_dt);
pn_Month := EXTRACT (MONTH FROM pd_dt);
IF pn_Month = 1 AND pn_Day =1 THEN
RETURN 'Nowy Rok';
ELSIF pn_Month = 1 AND pn_Day =6 THEN
RETURN 'Święto Trzech Króli' ;
ELSIF pn_Month = 3 AND pn_Day =27 THEN
RETURN 'Wielkanoc' ;
ELSIF pn_Month = 3 AND pn_Day =28 THEN
RETURN 'Poniedziałek Wielkanocny';
ELSIF pn_Month = 5 AND pn_Day =1 THEN
RETURN 'Święto Pracy';
ELSIF pn_Month = 5 AND pn_Day =3 THEN
RETURN 'Święto Konstytucji 3 Maja';
ELSIF pn_Month = 5 AND pn_Day =15 THEN
RETURN 'Zielone Świątki';
ELSIF pn_Month = 5 AND pn_Day =26 THEN
RETURN 'Boże Ciało';
ELSIF pn_Month = 8 AND pn_Day =15 THEN
RETURN 'Wniebowzięcie NMP';
ELSIF pn_Month = 11 AND pn_Day =1 THEN
RETURN 'Wszystkich Świętych';
ELSIF pn_Month = 11 AND pn_Day =11 THEN
RETURN 'Święto Niepodległości';
ELSIF pn_Month = 12 AND pn_Day IN (25, 26) THEN
RETURN 'Boże Narodzenie';
END IF;
RETURN NULL;
END;
SELECT
DZIEN , DZIEN_TYGODNIA, SWIETO,
DZIEN_ROBOCZY
FROM (SELECT 0 a FROM DUAL)
MODEL
DIMENSION BY (0 lp)
MEASURES (TO_DATE('2016-01-01', 'YYYY-MM-DD') DZIEN,
CAST(NULL AS VARCHAR2(30 CHAR)) SWIETO,
CAST(NULL AS VARCHAR2(30 CHAR)) DZIEN_TYGODNIA,
CAST( NULL AS NUMBER(1)) DZIEN_ROBOCZY )
RULES
ITERATE (10000000) UNTIL ( ITERATION_NUMBER = 365)
(
DZIEN[ ITERATION_NUMBER ] = DZIEN[0] + ITERATION_NUMBER,
SWIETO[ ITERATION_NUMBER ] = Fdb_JestSwieto( DZIEN [ ITERATION_NUMBER ]),
DZIEN_TYGODNIA[ ITERATION_NUMBER ] = TO_CHAR(DZIEN [ ITERATION_NUMBER ], 'DAY'),
DZIEN_ROBOCZY [ ITERATION_NUMBER ] = CASE WHEN SWIETO [ ITERATION_NUMBER ] IS NOT NULL OR TO_CHAR(DZIEN [ ITERATION_NUMBER ], 'D') IN ('6','7')
THEN 0
ELSE 1
END
)
Blog programisty w Oracle PL/SQL
Jest to blog eksperymentatora programisty w PL/SQL dla Oracle. Wszystkie kody tutaj zamieszczone mogą być dowolnie wykorzystywane i zmieniane. A jeśli Ktoś z Gości znajdzie błąd, będę niezwykle wdzięczny...
Zapisz
Zapisz
Szukaj na tym blogu
poniedziałek, 28 grudnia 2015
niedziela, 22 listopada 2015
Uproszczony kalkulator wynagrodzeń za rok 2015 n. e.
Poniżej zamieszczam kolejny przykład wykorzystania klauzuli model w wersji iteracyjnej, który przelicza wynagrodzenia brutto z umowy o pracę na poszczególne składniki po stronie pracownika w tym kwotę netto. Nie obsługuje progresji podatkowej ani ograniczenia rocznej podstawy
wymiaru składek na ubezpieczenie
emerytalne i rentowe (tzw.
trzydziestokrotności prognozowanego
przeciętnego wynagrodzenia
miesięcznego w gospodarce
narodowej na dany rok
kalendarzowy).
Istotą przykładu jest pokazanie w jaki elegancki sposób, zbliżony do obliczeń w MS Excel, można przedstawić niektóre obliczenia finansowe, a zwłaszcza odwoływanie się do wcześniej obliczonych komórek. Zapis taki jest intuicyjny i łatwy do modyfikacji..Arkusz jest jednowymiarowy w ujęciu klauzuli model, podstawową miarą (do modyfikowania przez użytkownika) jest kwota brutto zaznaczona na zielono. Użytkownik w łatwy sposób może dodawać własne miary np. obciążenia po stronie pracodawcy lub w inny sposób rozszerzyć poniższe zapytanie.
SELECT i lp,
DATA_WYPLATY AS "Data wypłaty" ,
KWOTA_BRUTTO,
SKLADKA_EMERYTALNA_PRACOWNIK,
SKLADKA_RENTOWA_PRACOWNIK,
SKLADKA_WYPADKOWA_PRACOWNIK,
SKLADKI_PRACOWNIK_RAZEM,
DOCHOD_PRACOWNIKA,
SKLADKA_ZDROWOTNA ,
ZALICZKA_PODATEK,
WYNAGRODZENIE_NETTO
FROM (SELECT 1 FROM DUAL)
MODEL
DIMENSION BY (0 d)
MEASURES (6000 KWOTA_BRUTTO,
TO_DATE( '2015-01-01', 'YYYY-MM-DD') DATA_WYPLATY , 0 i,
0 SKLADKA_EMERYTALNA_PRACOWNIK,
0 SKLADKA_RENTOWA_PRACOWNIK,
0 SKLADKA_WYPADKOWA_PRACOWNIK,
0 SKLADKI_PRACOWNIK_RAZEM,
111.25 KOSZT_UZYSKANIA_PRZYCHODU /*Pracownik niedojeżdżający*/ ,
0 DOCHOD_PRACOWNIKA,
0 SKLADKA_ZDROWOTNA,
0 ZALICZKA_PODATEK,
0 WYNAGRODZENIE_NETTO )
RULES
ITERATE (100) UNTIL (ITERATION_NUMBER >= 11)
(
i[ITERATION_NUMBER] = ITERATION_NUMBER +1,
DATA_WYPLATY [ITERATION_NUMBER ] = LAST_DAY(DECODE( DATA_WYPLATY [ITERATION_NUMBER -1], NULL, DATA_WYPLATY [ITERATION_NUMBER],
ADD_MONTHS( DATA_WYPLATY [ITERATION_NUMBER -1], 1))),
KWOTA_BRUTTO[ITERATION_NUMBER ] = KWOTA_BRUTTO[ 0 ],
SKLADKA_EMERYTALNA_PRACOWNIK[ ITERATION_NUMBER ] = ROUND(KWOTA_BRUTTO[ITERATION_NUMBER ] * 0.0976, 2),
SKLADKA_RENTOWA_PRACOWNIK[ ITERATION_NUMBER ] = ROUND(KWOTA_BRUTTO[ITERATION_NUMBER ] * 0.015, 2),
SKLADKA_WYPADKOWA_PRACOWNIK[ ITERATION_NUMBER ] = ROUND(KWOTA_BRUTTO[ITERATION_NUMBER ] * 0.0245, 2) ,
SKLADKI_PRACOWNIK_RAZEM[ITERATION_NUMBER ]
= SKLADKA_EMERYTALNA_PRACOWNIK[ ITERATION_NUMBER ] + SKLADKA_RENTOWA_PRACOWNIK[ ITERATION_NUMBER ]
+SKLADKA_WYPADKOWA_PRACOWNIK[ ITERATION_NUMBER ],
DOCHOD_PRACOWNIKA[ITERATION_NUMBER ] = ROUND( KWOTA_BRUTTO[ITERATION_NUMBER ] -SKLADKI_PRACOWNIK_RAZEM[ITERATION_NUMBER ] - KOSZT_UZYSKANIA_PRZYCHODU[0]),
SKLADKA_ZDROWOTNA[ITERATION_NUMBER ] = ROUND( (KWOTA_BRUTTO[ITERATION_NUMBER ] - SKLADKI_PRACOWNIK_RAZEM[0]) * 0.09, 2),
ZALICZKA_PODATEK[ITERATION_NUMBER ] = ROUND(DOCHOD_PRACOWNIKA[ITERATION_NUMBER ] * 0.18 - 46.33 - ROUND( (DOCHOD_PRACOWNIKA[ITERATION_NUMBER ] + KOSZT_UZYSKANIA_PRZYCHODU[0]) * 0.0775)),
WYNAGRODZENIE_NETTO[ITERATION_NUMBER ]= KWOTA_BRUTTO [ITERATION_NUMBER ] - SKLADKI_PRACOWNIK_RAZEM[ITERATION_NUMBER ] - SKLADKA_ZDROWOTNA[ITERATION_NUMBER ] - ZALICZKA_PODATEK[ITERATION_NUMBER ]
)
Istotą przykładu jest pokazanie w jaki elegancki sposób, zbliżony do obliczeń w MS Excel, można przedstawić niektóre obliczenia finansowe, a zwłaszcza odwoływanie się do wcześniej obliczonych komórek. Zapis taki jest intuicyjny i łatwy do modyfikacji..Arkusz jest jednowymiarowy w ujęciu klauzuli model, podstawową miarą (do modyfikowania przez użytkownika) jest kwota brutto zaznaczona na zielono. Użytkownik w łatwy sposób może dodawać własne miary np. obciążenia po stronie pracodawcy lub w inny sposób rozszerzyć poniższe zapytanie.
SELECT i lp,
DATA_WYPLATY AS "Data wypłaty" ,
KWOTA_BRUTTO,
SKLADKA_EMERYTALNA_PRACOWNIK,
SKLADKA_RENTOWA_PRACOWNIK,
SKLADKA_WYPADKOWA_PRACOWNIK,
SKLADKI_PRACOWNIK_RAZEM,
DOCHOD_PRACOWNIKA,
SKLADKA_ZDROWOTNA ,
ZALICZKA_PODATEK,
WYNAGRODZENIE_NETTO
FROM (SELECT 1 FROM DUAL)
MODEL
DIMENSION BY (0 d)
MEASURES (6000 KWOTA_BRUTTO,
TO_DATE( '2015-01-01', 'YYYY-MM-DD') DATA_WYPLATY , 0 i,
0 SKLADKA_EMERYTALNA_PRACOWNIK,
0 SKLADKA_RENTOWA_PRACOWNIK,
0 SKLADKA_WYPADKOWA_PRACOWNIK,
0 SKLADKI_PRACOWNIK_RAZEM,
111.25 KOSZT_UZYSKANIA_PRZYCHODU /*Pracownik niedojeżdżający*/ ,
0 DOCHOD_PRACOWNIKA,
0 SKLADKA_ZDROWOTNA,
0 ZALICZKA_PODATEK,
0 WYNAGRODZENIE_NETTO )
RULES
ITERATE (100) UNTIL (ITERATION_NUMBER >= 11)
(
i[ITERATION_NUMBER] = ITERATION_NUMBER +1,
DATA_WYPLATY [ITERATION_NUMBER ] = LAST_DAY(DECODE( DATA_WYPLATY [ITERATION_NUMBER -1], NULL, DATA_WYPLATY [ITERATION_NUMBER],
ADD_MONTHS( DATA_WYPLATY [ITERATION_NUMBER -1], 1))),
KWOTA_BRUTTO[ITERATION_NUMBER ] = KWOTA_BRUTTO[ 0 ],
SKLADKA_EMERYTALNA_PRACOWNIK[ ITERATION_NUMBER ] = ROUND(KWOTA_BRUTTO[ITERATION_NUMBER ] * 0.0976, 2),
SKLADKA_RENTOWA_PRACOWNIK[ ITERATION_NUMBER ] = ROUND(KWOTA_BRUTTO[ITERATION_NUMBER ] * 0.015, 2),
SKLADKA_WYPADKOWA_PRACOWNIK[ ITERATION_NUMBER ] = ROUND(KWOTA_BRUTTO[ITERATION_NUMBER ] * 0.0245, 2) ,
SKLADKI_PRACOWNIK_RAZEM[ITERATION_NUMBER ]
= SKLADKA_EMERYTALNA_PRACOWNIK[ ITERATION_NUMBER ] + SKLADKA_RENTOWA_PRACOWNIK[ ITERATION_NUMBER ]
+SKLADKA_WYPADKOWA_PRACOWNIK[ ITERATION_NUMBER ],
DOCHOD_PRACOWNIKA[ITERATION_NUMBER ] = ROUND( KWOTA_BRUTTO[ITERATION_NUMBER ] -SKLADKI_PRACOWNIK_RAZEM[ITERATION_NUMBER ] - KOSZT_UZYSKANIA_PRZYCHODU[0]),
SKLADKA_ZDROWOTNA[ITERATION_NUMBER ] = ROUND( (KWOTA_BRUTTO[ITERATION_NUMBER ] - SKLADKI_PRACOWNIK_RAZEM[0]) * 0.09, 2),
ZALICZKA_PODATEK[ITERATION_NUMBER ] = ROUND(DOCHOD_PRACOWNIKA[ITERATION_NUMBER ] * 0.18 - 46.33 - ROUND( (DOCHOD_PRACOWNIKA[ITERATION_NUMBER ] + KOSZT_UZYSKANIA_PRZYCHODU[0]) * 0.0775)),
WYNAGRODZENIE_NETTO[ITERATION_NUMBER ]= KWOTA_BRUTTO [ITERATION_NUMBER ] - SKLADKI_PRACOWNIK_RAZEM[ITERATION_NUMBER ] - SKLADKA_ZDROWOTNA[ITERATION_NUMBER ] - ZALICZKA_PODATEK[ITERATION_NUMBER ]
)
poniedziałek, 26 października 2015
Jeden parametr sekwencji a wydajność przy wstawianiu
Sekwencja zapewnia unikalność identyfikatorów, ale nie ich ciągłość na zbiorze liczb całkowitych. W zależności od liczby wstawianych rekordów warto buforować w pamięci kolejne wartości sekwencji, zwłaszcza przy masowym wstawianiu. Stwórzmy sekwencję
CREATE SEQUENCE SEQ_START START WITH 1 CACHE 2;
oraz prostą tabelę, gdzie w polu ID będziemy wstawiać kolejne wartości sekwencji
CREATE TABLE TBL_TEST
(
ID NUMBER NOT NULL,
DESCRIPTION_1 VARCHAR2(40 CHAR) NOT NULL,
DESCRIPTION_2 VARCHAR2(40 CHAR) NOT NULL,
DESCRIPTION_3 VARCHAR2(40 CHAR) NOT NULL
);
Poniższy blok wstawia do pustej tabeli sto tysięcy rekordów bez korzystania z sekwencji. Uśredniony czas wykonania to 4,3 sekundy..
DECLARE
vn_Start NUMBER;
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE TBL_TEST';
vn_Start := dbms_utility.get_time;
FOR EL IN 1..100000
LOOP
INSERT INTO ALTAIRDB.TBL_TEST (
ID, DESCRIPTION_1, DESCRIPTION_2,
DESCRIPTION_3)
VALUES ( EL /* ID */,
'DESCRPTION_1 ' || EL /* DESCRIPTION_1 */,
'DESCRPTION_2 ' || EL /* DESCRIPTION_2 */,
'DESCRPTION_3 ' || EL /* DESCRIPTION_3 */ );
END LOOP;
COMMIT;
dbms_output.put_line( ROUND( (dbms_utility.get_time- vn_start)/100, 2 ) || ' sekund...' );
END;
W poniższym przykładzie wstawiamy wielokrotnie do pustej tabeli po sto tysięcy rekordów z wykorzystaniem sekwencji z różnym parametrem buforowania.
DECLARE.
vn_Start NUMBER;
BEGIN
FOR EL_OUT IN (SELECT COLUMN_VALUE AS CACHE_SIZE,
DECODE( COLUMN_VALUE, 1 , 'NOCACHE', 'CACHE ' || COLUMN_VALUE) AS CACHE_DDL
FROM TABLE(SYS.ODCINUMBERLIST(1, 2,5,10, 20, 50, 100, 500, 1000, 5000, 10000)))
LOOP
BEGIN
EXECUTE IMMEDIATE 'DROP SEQUENCE SEQ_START';
EXCEPTION WHEN OTHERS THEN
NULL;
END;
EXECUTE IMMEDIATE 'TRUNCATE TABLE TBL_TEST';
EXECUTE IMMEDIATE 'CREATE SEQUENCE SEQ_START START WITH 1 ' || EL_OUT.CACHE_DDL;
vn_Start := dbms_utility.get_time;
FOR EL_IN IN 1..100000
LOOP
INSERT INTO TBL_TEST (
ID, DESCRIPTION_1, DESCRIPTION_2,
DESCRIPTION_3)
VALUES ( SEQ_START.nextval /* ID */,
'DESCRPTION_1 ' || EL_IN /* DESCRIPTION_1 */,
'DESCRPTION_2 ' || EL_IN /* DESCRIPTION_2 */,
'DESCRPTION_3 ' || EL_IN /* DESCRIPTION_3 */ );
END LOOP;
COMMIT;
dbms_output.put_line( EL_OUT.CACHE_DDL || ' - ' || ROUND( (dbms_utility.get_time- vn_start)/100, 2 ) || ' sekund...' );
END LOOP;
END;
Wyniki:
NOCACHE - 26,9 sekund...
CACHE 2 - 17,82 sekund...
CACHE 5 - 10,82 sekund...
CACHE 10 - 8,78 sekund...
CACHE 20 - 7,79 sekund...
CACHE 50 - 7,17 sekund...
CACHE 100 - 7,01 sekund...
CACHE 500 - 6,94 sekund...
CACHE 1000 - 6,95 sekund...
CACHE 5000 - 6,87 sekund...
CACHE 10000 - 6,56 sekund...
To jest przykład na to, jak mało ważne parametry mogą istotnie zmniejszyć wydajność,zwłaszcza dla masowego przetwarzania...
CREATE SEQUENCE SEQ_START START WITH 1 CACHE 2;
oraz prostą tabelę, gdzie w polu ID będziemy wstawiać kolejne wartości sekwencji
CREATE TABLE TBL_TEST
(
ID NUMBER NOT NULL,
DESCRIPTION_1 VARCHAR2(40 CHAR) NOT NULL,
DESCRIPTION_2 VARCHAR2(40 CHAR) NOT NULL,
DESCRIPTION_3 VARCHAR2(40 CHAR) NOT NULL
);
Poniższy blok wstawia do pustej tabeli sto tysięcy rekordów bez korzystania z sekwencji. Uśredniony czas wykonania to 4,3 sekundy..
DECLARE
vn_Start NUMBER;
BEGIN
EXECUTE IMMEDIATE 'TRUNCATE TABLE TBL_TEST';
vn_Start := dbms_utility.get_time;
FOR EL IN 1..100000
LOOP
INSERT INTO ALTAIRDB.TBL_TEST (
ID, DESCRIPTION_1, DESCRIPTION_2,
DESCRIPTION_3)
VALUES ( EL /* ID */,
'DESCRPTION_1 ' || EL /* DESCRIPTION_1 */,
'DESCRPTION_2 ' || EL /* DESCRIPTION_2 */,
'DESCRPTION_3 ' || EL /* DESCRIPTION_3 */ );
END LOOP;
COMMIT;
dbms_output.put_line( ROUND( (dbms_utility.get_time- vn_start)/100, 2 ) || ' sekund...' );
END;
W poniższym przykładzie wstawiamy wielokrotnie do pustej tabeli po sto tysięcy rekordów z wykorzystaniem sekwencji z różnym parametrem buforowania.
DECLARE.
vn_Start NUMBER;
BEGIN
FOR EL_OUT IN (SELECT COLUMN_VALUE AS CACHE_SIZE,
DECODE( COLUMN_VALUE, 1 , 'NOCACHE', 'CACHE ' || COLUMN_VALUE) AS CACHE_DDL
FROM TABLE(SYS.ODCINUMBERLIST(1, 2,5,10, 20, 50, 100, 500, 1000, 5000, 10000)))
LOOP
BEGIN
EXECUTE IMMEDIATE 'DROP SEQUENCE SEQ_START';
EXCEPTION WHEN OTHERS THEN
NULL;
END;
EXECUTE IMMEDIATE 'TRUNCATE TABLE TBL_TEST';
EXECUTE IMMEDIATE 'CREATE SEQUENCE SEQ_START START WITH 1 ' || EL_OUT.CACHE_DDL;
vn_Start := dbms_utility.get_time;
FOR EL_IN IN 1..100000
LOOP
INSERT INTO TBL_TEST (
ID, DESCRIPTION_1, DESCRIPTION_2,
DESCRIPTION_3)
VALUES ( SEQ_START.nextval /* ID */,
'DESCRPTION_1 ' || EL_IN /* DESCRIPTION_1 */,
'DESCRPTION_2 ' || EL_IN /* DESCRIPTION_2 */,
'DESCRPTION_3 ' || EL_IN /* DESCRIPTION_3 */ );
END LOOP;
COMMIT;
dbms_output.put_line( EL_OUT.CACHE_DDL || ' - ' || ROUND( (dbms_utility.get_time- vn_start)/100, 2 ) || ' sekund...' );
END LOOP;
END;
Wyniki:
NOCACHE - 26,9 sekund...
CACHE 2 - 17,82 sekund...
CACHE 5 - 10,82 sekund...
CACHE 10 - 8,78 sekund...
CACHE 20 - 7,79 sekund...
CACHE 50 - 7,17 sekund...
CACHE 100 - 7,01 sekund...
CACHE 500 - 6,94 sekund...
CACHE 1000 - 6,95 sekund...
CACHE 5000 - 6,87 sekund...
CACHE 10000 - 6,56 sekund...
To jest przykład na to, jak mało ważne parametry mogą istotnie zmniejszyć wydajność,zwłaszcza dla masowego przetwarzania...
poniedziałek, 12 października 2015
Algorytmy obliczeniowe - walka wydajności z przewidywalnością
Napiszmy krótki kawałek kodu, który wykonuje niezbyt sensowne obliczenia korzystając z wbudowanych funkcji matematycznych. Kod zostanie wykonany na serwerze Exadata
DECLARE
vn_Pom NUMBER :=0;
vn_Start NUMBER;
BEGIN
vn_Start := dbms_utility.get_time;
vn_Pom := 1.2;
FOR EL IN 1..100000
LOOP
vn_Pom := COS(SIN(vn_Pom));
vn_Pom :=1+ vn_Pom + POWER(2, vn_Pom);
vn_Pom := LOG(2, ABS(vn_Pom) + 3);
END LOOP;
dbms_output.put_line( ROUND( (dbms_utility.get_time- vn_start)/100, 2 ) || ' sekund...' );
END;
Średni czas wykonania dla trzech przebiegów to 9,02 sekundy.
Zmieńmy teraz jedną podkreśloną linię w powyższym anonimowym bloku kodu.
vn_Pom BINARY_DOUBLE :=0;
Średni czas wykonania zmniejszył się do 0,06 sekundy.
Skąd wynika ta zmiana??
W pierwszym przypadku typ NUMBER jest uniwersalny i niezależny od maszyny. Jest obsługiwany za pomocą dołączonym bibliotek napisanych w C i na każdym serwerze dla jednakowych operacji powinny być zwracane jednakowe wartości. Bardzo czasochłonne są funkcje matematyczne wykorzystujące prawdopodobnie rozwinięcie w szereg funkcji trygonometrycznych, co jest dość zasobożerne. Ale zawsze otrzymamy ten sam wynik !!! Jest to cecha niezwykle istotna w przypadku rozproszonych systemów transakcyjnych korzystających z niejednorodnej infrastruktury sprzętowej. Jest to emulacja niezależna od typu serwera.
Natomiast w drugim przypadku deklaracja zmiennej vn_Pom BINARY_DOUBLE :=0 informuje serwer, że obliczenia będą przeprowadzone przy wykorzystaniu koprocesora matematycznego w obrębie procesora serwera. To odpowiednik typu double w C przy wykorzystaniu koprocesora. Jet to rozwiązanie bardzo szybkie, tym niemniej zachodzi uzasadniona obawa, ze dla rożnych platform sprzętowych, wyniki obliczeń będą się różnić ostatnimi cyframi. Należy tez zauważyć, że koprocesory nie obsługują wartości NULL, która to jest wysokopoziomowym wynalazkiem, stąd przymusowa inicjalizacja zmiennej. Ma to też swoją zaletę - taki kod na etapie testowania zawsze będzie generować wyjątki, kiedy napotka wartość NULL i ułatwi wykrycie miejsc, skąd przechodzą wartości niezainicjalizowane..
DECLARE
vn_Pom NUMBER :=0;
vn_Start NUMBER;
BEGIN
vn_Start := dbms_utility.get_time;
vn_Pom := 1.2;
FOR EL IN 1..100000
LOOP
vn_Pom := COS(SIN(vn_Pom));
vn_Pom :=1+ vn_Pom + POWER(2, vn_Pom);
vn_Pom := LOG(2, ABS(vn_Pom) + 3);
END LOOP;
dbms_output.put_line( ROUND( (dbms_utility.get_time- vn_start)/100, 2 ) || ' sekund...' );
END;
Średni czas wykonania dla trzech przebiegów to 9,02 sekundy.
Zmieńmy teraz jedną podkreśloną linię w powyższym anonimowym bloku kodu.
vn_Pom BINARY_DOUBLE :=0;
Średni czas wykonania zmniejszył się do 0,06 sekundy.
Skąd wynika ta zmiana??
W pierwszym przypadku typ NUMBER jest uniwersalny i niezależny od maszyny. Jest obsługiwany za pomocą dołączonym bibliotek napisanych w C i na każdym serwerze dla jednakowych operacji powinny być zwracane jednakowe wartości. Bardzo czasochłonne są funkcje matematyczne wykorzystujące prawdopodobnie rozwinięcie w szereg funkcji trygonometrycznych, co jest dość zasobożerne. Ale zawsze otrzymamy ten sam wynik !!! Jest to cecha niezwykle istotna w przypadku rozproszonych systemów transakcyjnych korzystających z niejednorodnej infrastruktury sprzętowej. Jest to emulacja niezależna od typu serwera.
Natomiast w drugim przypadku deklaracja zmiennej vn_Pom BINARY_DOUBLE :=0 informuje serwer, że obliczenia będą przeprowadzone przy wykorzystaniu koprocesora matematycznego w obrębie procesora serwera. To odpowiednik typu double w C przy wykorzystaniu koprocesora. Jet to rozwiązanie bardzo szybkie, tym niemniej zachodzi uzasadniona obawa, ze dla rożnych platform sprzętowych, wyniki obliczeń będą się różnić ostatnimi cyframi. Należy tez zauważyć, że koprocesory nie obsługują wartości NULL, która to jest wysokopoziomowym wynalazkiem, stąd przymusowa inicjalizacja zmiennej. Ma to też swoją zaletę - taki kod na etapie testowania zawsze będzie generować wyjątki, kiedy napotka wartość NULL i ułatwi wykrycie miejsc, skąd przechodzą wartości niezainicjalizowane..
Subskrybuj:
Posty (Atom)