SQL: conteggio preventivo dei record da leggere

In alcune situazioni può essere necessario conoscere in anticipo il numero di record che si stanno per leggere e presentare all’utente. Pensiamo per esempio ad applicazioni realizzate per il Web, dove un elenco viene mostrato a gruppi e a video compaiono dei pulsanti per spostarsi da una pagina all’altra. Esempio:

Qui si nota come compaia già il numero totale di elementi visualizzabili, mostrati a gruppi di 10 per volta.
Per estrarre i dati è necessario definire un cursore SQL e caricare tutti i record letti in una schiera di DS, gestendo poi il posizionamento pagina per pagina.
Il conteggio si può ottenere impostando in una variabile il risultato di una COUNT(*) a fronte di uno statement che contenga gli stessi criteri di estrazione del cursore definito in precedenza.
Esiste però una soluzione più efficace, che ci evita di scrivere due istruzioni praticamente uguali…
L’istruzione GET DIAGNOSTICS restituisce numerose informazioni relative all’ultima istruzione SQL eseguita. Qui la documentazione dettagliata da IBM. Nel nostro caso, l’informazione che ci interessa è DB2_NUMBER_ROWS che, eseguita dopo la OPEN di un cursore, contiene il numero di righe della tabella risultante dalla selezione.

Exec SQL
	GET DIAGNOSTICS :righe = DB2_NUMBER_ROWS ;

Ecco un esempio di semplice programma che dichiara una query SQL e reperisce il numero di righe:

        //                                                         
        // Conteggio righe con GET DIAGNOSTICS - DB2_NUMBER_ROWS   
        //                                                         
                                                                   
        // *ENTRY PLIST                                            
        dcl-pi FAQ40001 ;                                          
        end-pi;                                                    
        dcl-s  righe       packed(5:0);                            
                                                                   
        *inLR=*on;                                                 
        Exec SQL                                                   
          SET OPTION Commit = *none,                               
                     CloSqlCsr=*EndMod,                            
                     DlyPrp=*YES                                   
          ;                                                        
                                                                   
         Exec SQL                                                  
           DECLARE c1 CURSOR FOR                                   
           SELECT                                                  
           m.tipoArt, m.articolo, descrizione,                     
           codMag, dataMovimento, segno, m.um, quantita            
           FROM  mduca1.movmag00f m                                
           LEFT JOIN mduca1.anaart00f a ON                         
            (m.tipoArt, m.Articolo)=(a.tipoArt, a.Articolo)     
           WHERE quantita > 50                                  
           ;                                                    
         Exec SQL                                               
           OPEN c1;                                             
         Exec SQL                                               
           GET DIAGNOSTICS :righe = DB2_NUMBER_ROWS;            
                                                                
         dsply ('RIGHE = ' + %char(righe)) ;                    
                                                                
        return;                                                 

Lanciando il programma a fronte di un database di test, questo è il risultato:

 DSPLY  RIGHE = 409                  
                                     
 Premere Invio per continuare.                                          

Andiamo a verificare eseguendo la stessa query da una sessione SQL, selezionando il solo conteggio dei record:

Ma… c’è qualcosa che non quadra ! Per una stessa query il programma trova 408 righe, e l’SQL ne trova 448 ?
La risposta a questa “stranezza” la dà il manuale IBM: per cursori di tipo “dinamico” (come quello visto sopra) il valore restituito in DB2_NUMBER_ROWS è da considerarsi una approssimazione, in quanto eventuali righe inserite o cancellate possono avere impatto sul set di risultati finale.
Per ottenere il numero esatto di righe occorre quindi definire esplicitamente il cursore come “INSENSITIVE”; in questo modo il set di risultati viene “congelato” al momento dell’apertura del cursore e le eventuali modifiche alle tabelle refenziate nell’istruzione non avranno effetto. I cursori di tipo INSENSITIVE non possono essere usati per operazioni di aggiornamento o cancellazione.

         Exec SQL                                                  
           DECLARE c1 INSENSITIVE CURSOR FOR   
			ecc...

Rilanciamo il programma dopo la modifica… ed ora finalmente tutto quadra !

                    Visualizzazione messaggi del programma                     
                                                                               
DSPLY  RIGHE = 448                                                 
                                                                                
Premere Invio per continuare.    

Attenzione: se si gestisce la paginazione direttamente nel cursore SQL (con le parole chiave LIMIT o FETCH FIRST n ROWS ONLY e OFFSET), GET DIAGNOSTICS non restituirà il totale delle righe del set di risultati, ma solo quelle specificate nella clausola LIMIT. Per ottenere il totale sarà quindi necessario definire un secondo cursore senza la clausola di limitazione del numero di righe lette.

Related Posts
DB2 for i SQL – Stringhe – POSSTR-LOCATE-LOCATE_IN_STRING (IT)

Introduzione Spesso, nelle nostre applicazioni, abbiamo la necessità di lavorare con le stringhe di testo e l'SQL del DB2 può Read more

DB2 for i & SQL – FAQ & Howto (Part. 1) (IT)

Database DB2 e SQL ... forse lo strumento più potente e completo che abbiamo sulla piattaforma IBM i: ecco una Read more

Annuncio IBM i 7.4

Arriva direttamente con l'uovo di Pasqua questo annuncio IBM per le novità della versione IBM i 7.4, versione iNext secondo Read more

Generated Always Columns – Approfondimenti (IT)

Introduzione "Generated Always Column": sono colonne, campi, di una tabella il cui contenuto è controllato direttamente dal sistema ... e Read more

Recent Posts

VsCode Extension: Bob Cozzi’s RPG IV to RPG Free Conversion

L’estensione “RPG IV to Free Format Conversion” sviluppata da Bob Cozzi (Cozzi Research) è pensata per semplificare la conversione di…

6 mesi ago

IBM i & SQL Tips #010 – Localizzare programmi nella Call Stack con STACK_INFO

Ciao a tutti, oggi voglio segnalarvi un altro interessante contributo di Massimo Duca, parte della sua ormai nota serie IBM…

6 mesi ago

Display file DDS Edit per VsCode, nuova preview.

Incuriosito da alcuni messaggi di Cristian Larsen su Linkedin (New Release - Display File DDS Edit v.0.10.1) ho voluto scaricare…

6 mesi ago

Project Bob: il nuovo strumento AI di IBM per sviluppo COBOL su IBM Z e RPG su IBM i

Ciao a tutti,oggi voglio segnalarvi un annuncio che potrebbe segnare una svolta per lo sviluppo applicativo su ambienti IBM: Project…

6 mesi ago

IBM i & SQL Tips #6: chiamare API REST e analizzare le risposte JSON con SQL

Voglio segnalarvi un nuovo articolo molto interessante di Massimo Duca nella serie IBM i & SQL Tips. In questo sesto…

7 mesi ago

Come funziona il passaggio di parametri a un programma IBM i (RPG / Cobol)

Ciao a tutti, voglio segnalarvi un post molto utile di Marco Riva sul suo sito Markonetools, in cui spiega in…

7 mesi ago