teisipäev, 12. veebruar 2019

Lehekülgede pagineerimine ORACLE-s ühe SELECT lause kasutamisega ainult


Eriti REST rakenduste puhul tuleb sageli pagineerimine teha andmebaasi poolel.

Läbi tuleb teha selleks kaks etappi
1. Saada teade kirjete koguarv
2. Võtta vajalik vahemik välja

ORACLE WITH konstruktsiooni tehes saab selle tehe ühe SELECT lausega ainult, mis juhtudel, kui SELECT lause ise võtab kaua aega annab märgatava ajalise kokkuhoiu

Näide, kus annad ette lehekülje suuruse ja mitmendat lehekülge tahad

SET SERVEROUTPUT ON
DECLARE i_page_size INTEGER;  --lehekülje suurus
i_page_no INTEGER; --mitme lehekülg kuvada
BEGIN
i_page_size:=5;
i_page_no:=2;
FOR ss IN (
 WITH Tu AS
        (  SELECT ROWNUM AS jrk, dde.ID, dde.SUBJECT_ID, dde.OCCUP_NAME
                , CEIL((ROWNUM)/i_page_size) AS PAGE_NUMBER --arvutab lehekülje numbri
            FROM
            (  SELECT ID, SUBJECT_ID, OCCUP_NAME FROM SUB_EMPLOYER ORDER BY OCCUP_NAME DESC ) dde
     )       
     SELECT Tu.jrk, Tu.ID, Tu.SUBJECT_ID, Tu.OCCUP_NAME,Tu.PAGE_NUMBER
       ,(SELECT MAX(Tu.jrk) FROM Tu ) AS kokku_kirjeid
       FROM Tu
         WHERE Tu.PAGE_NUMBER = i_page_no  --pagineerimine
         ORDER BY Tu.jrk)
LOOP
    DBMS_OUTPUT.PUT_LINE ( SS.JRK || ' ' || ss.OCCUP_NAME || ' ' || ss.SUBJECT_ID  || ' ' || ss.kokku_kirjeid);
END LOOP;
END;

WITH konstruktsiooni osa, saab kätte kirje järjekorra numbri ja CEIL funktsiooniga arvutab lehekülje numbri

ROWNUM AS jrk, CEIL((ROWNUM)/i_page_size) AS PAGE_NUMBER
      
 hilisemas välises päringus saab eraldi tulbast kätte ka kirjete koguarvu
(SELECT MAX(Tu.jrk) FROM Tu ) AS kokku_kirjeid



Mõnikord vaja saada lisaks eraldi teada ka kirjete ja lehekülgede koguarv siis kasutab CURSOR-i kuhu lisab ette esimeseks lehekülgede ja ridade koguarvuga rea ning FETCH-ib selle välja enne tagastamist. Esimese rea väljatõmbamisel saad lehekülgede ja kirjete koguarvu teada
o_totalPages:=nvl(ceil(o_totalRows/i_page_size),0); --arvutame lehekülgede arvu
o_totalRows:=nvl(o_totalRows,0); --nvl et saaks ridade puudumisel number 0-i välja



SET SERVEROUTPUT ON
DECLARE i_page_size INTEGER;  --lehekülje suurus
i_page_no INTEGER; --mitmes lehekülg kuvada
o_cursor sys_refcursor;  --siia paneme tulemused
o_totalPages INTEGER; --mitu lehekülge kokku
o_totalRows INTEGER; --mitu rida kokku

v_int integer; --abimuutuja
v_int2 integer; --abimuutuja
v_lehekulg integer; --abimuutuja
v_subject_id varchar2(8000); --abimuutuja
v_occup_name varchar2(8000); --abimuutuja
BEGIN
i_page_size:=5;
i_page_no:=2;

open o_cursor for
 WITH Tu AS
        ( 
        SELECT dde2.jrk, dde2.ID, dde2.SUBJECT_ID, dde2.OCCUP_NAME, dde2.PAGE_NUMBER
        FROM (
        SELECT ROWNUM AS jrk, dde.ID, dde.SUBJECT_ID, dde.OCCUP_NAME
                , CEIL((ROWNUM)/i_page_size) AS PAGE_NUMBER --arvutab lehekülje numbri
            FROM
            (  SELECT ID, SUBJECT_ID, OCCUP_NAME FROM SUB_EMPLOYER ORDER BY OCCUP_NAME DESC ) dde ) dde2
     )       
     SELECT Tu.jrk, Tu.ID, Tu.SUBJECT_ID, Tu.OCCUP_NAME,Tu.PAGE_NUMBER
       ,(SELECT MAX(Tu.jrk) FROM Tu ) AS kokku_kirjeid
       FROM Tu
         WHERE Tu.PAGE_NUMBER = i_page_no  --pagineerimine
       
    UNION --tekitame juurde esimese rea, sellekaudu saab fetch ridade koguarvu teada väljastuseks
        SELECT 0 as jrk, Tu.ID, Tu.SUBJECT_ID, Tu.OCCUP_NAME,Tu.PAGE_NUMBER
       ,(SELECT MAX(Tu.jrk) FROM Tu ) AS kokku_kirjeid FROM Tu WHERE Tu.jrk =1
         ORDER BY 1;  --et 0 as jrk rida esimeseks tuleks, mille FETCH-iga välja võtame
        
    --Esimese rea kursorist võib ära raisata, küsib sealt lehekülgede koguarvu ja kirjete koguarvu
     FETCH o_cursor INTO v_int, v_int2, v_subject_id, v_occup_name, v_lehekulg,o_totalRows;
             o_totalPages:=nvl(ceil(o_totalRows/i_page_size),0); --arvutame lehekülgede arvu
             o_totalRows:=nvl(o_totalRows,0); --nvl et saaks ridade puudumisel number 0-i välja
     IF o_totalRows=0 THEN   --anname tühja kursori välja, kui kirjeid üldse pole, tuleb viga ORA-01002: fetch out of sequence
        CLOSE o_cursor;
        OPEN o_cursor FOR
         SELECT 0 AS jrk , 0 AS ID, '' AS SUBJECT_ID, '' AS OCCUP_NAME, 0 AS PAGE_NUMBER, 0 AS kokku_kirjeid FROM dual;
     END IF;
     DBMS_OUTPUT.PUT_LINE ( 'Kokku kirjeid: ' || o_totalRows  || '   Kokku lehekülgi: ' || o_totalPages);
    
    
     LOOP
          FETCH o_cursor INTO  v_int, v_int2, v_subject_id, v_occup_name, v_lehekulg,o_totalRows;
             EXIT WHEN o_cursor%NOTFOUND;
          DBMS_OUTPUT.PUT_LINE ('Jrk: ' ||  v_int || ' ' || v_occup_name || ' ' || v_subject_id);
     END LOOP;
    
END;

Konstruktsioon 
.. UNION .. WHERE Tu.jrk =1 .. ORDER BY 1  teeb selle, et "tühi rida" mille võib ära visata tekib CURSOR-isse esimeseks, kust loeb välja kirjete ja lehekülgede koguarvu.

Kui kirjeid pole IF o_totalRows=0 THEN siis tekitab tühja üherealise CURSOR-i REST kliendi jaoks juhuks kui REST kliendile ei meeldi, et andmebaasist mitte kui midagi ei tagastata.