iSeries Sql – CTE Common Table Expressions – definizioni e utilizzo

Le Common Table Expressions (CTE) in SQL su IBM iSeries (AS/400), come in altri sistemi di gestione di database, sono una caratteristica potente e flessibile che permette di creare query temporanee e riutilizzabili all’interno di un’istruzione SQL più ampia.

Sono particolarmente utili per strutturare e semplificare query complesse, consentendo una maggiore leggibilità e manutenibilità del codice SQL.

Di seguito gli articoli della serie CTE (incluso questo corrente):

Descrizione Tecnica delle CTE

  1. Definizione: Una CTE è essenzialmente una query temporanea che esiste solo per la durata di una specifica istruzione SQL. È definita all’inizio di una query e può essere poi utilizzata come una normale tabella all’interno di quella stessa istruzione SQL.
     
  2. Sintassi: Una CTE viene definita utilizzando la clausola WITH, seguita dal nome della CTE, un elenco facoltativo di colonne e la query che popola la CTE. La sintassi generale è:
    WITH NomeCTE (Colonna1, Colonna2, ...) AS ( SELECT ... FROM ... WHERE ... ) SELECT * FROM NomeCTE;
  3. Riutilizzo: Una CTE può essere utilizzata più volte all’interno di un’unica istruzione SQL. Questo la rende utile per dividere query complesse in parti più piccole e gestibili, per evitare la duplicazione di codice e per migliorare la leggibilità.
     
  4. Query Ricorsive: Le CTE offrono la possibilità di scrivere query ricorsive, che sono particolarmente utili per lavorare con dati gerarchici o per eseguire calcoli iterativi. Una CTE ricorsiva si auto-referenzia per creare una serie di risultati ripetuti fino al raggiungimento di una condizione di terminazione.
     
  5. Performance: Mentre le CTE possono migliorare la leggibilità del codice, la loro efficienza in termini di prestazioni dipende dalla specifica implementazione del database e dalla natura della query. In alcuni casi, possono essere più efficienti di subquery equivalenti, ma non sempre.
     
  6. Compatibilità: Le CTE sono supportate dalla maggior parte dei moderni sistemi di gestione di database, inclusi IBM DB2 per iSeries, SQL Server, PostgreSQL, MySQL (versioni recenti), e altri.
     
  7. Applicazioni: Le CTE sono ampiamente utilizzate per semplificare query complesse, per eseguire query ricorsive, per preparare dati per l’aggregazione, per organizzare query con più passaggi, e per migliorare la struttura generale delle query SQL.

Considerazioni per IBM iSeries

  • Su IBM iSeries, la disponibilità e le caratteristiche specifiche delle CTE possono dipendere dalla versione del DB2.
     
  • È importante verificare la documentazione specifica del DB2 su iSeries per assicurarsi che tutte le funzionalità delle CTE siano supportate e per capire le migliori pratiche per il loro utilizzo.

vediamo alcuni esempi di utilizzo:

CTE Common Table Expression per assegnare numerazione over “partition by”

Descrizione Contesto e Obiettivo

La query in questione è progettata per operare su una tabella che registra le transazioni, identificate da un ID di transazione (TRANS_ID) e associate a una specifica data di movimento (DATE_MOV).

Mentre la tabella originale potrebbe contenere altre informazioni pertinenti, per lo scopo di questa query, l’attenzione è focalizzata esclusivamente su TRANS_ID e DATE_MOV.

L’obiettivo principale è generare un elenco in cui ogni transazione è elencata con un numero progressivo unico e la relativa data di movimento.

Questo numero progressivo inizia da 1 per ciascuna data di movimento e aumenta di uno per ogni transazione successiva che si verifica nella stessa data.

In altre parole, la query assegna un numero sequenziale alle transazioni, riavviando il conteggio da 1 per ogni nuova data di movimento, garantendo così che ciascuna transazione riceva un identificatore univoco all’interno del suo specifico giorno.

Query

WITH -- starting data
     W1 (TRANS_ID, DATE_MOV) AS (
         VALUES  ('T0011', '2024-01-10'), ('T0012', '2024-01-10'), ('T0013', '2024-01-10')
               , ('T0014', '2024-01-10'), ('T0015', '2024-01-10'), ('T0016', '2024-01-10')
               , ('T0017', '2024-01-10'), ('T0018', '2024-01-10'), ('T0019', '2024-01-10')
               , ('T0020', '2024-01-10'), ('T0023', '2024-01-10'), ('T0024', '2024-01-10')
               , ('T0025', '2024-01-10'), ('T0026', '2024-01-10'), ('T0127', '2024-01-10')
               , ('T0128', '2024-01-10'), ('T0129', '2024-01-10'), ('T0149', '2024-01-10')
               , ('T0150', '2024-01-11'), ('T0151', '2024-01-11'), ('T0152', '2024-01-11')
               , ('T0165', '2024-01-11'), ('T0166', '2024-01-11'), ('T0167', '2024-01-11')
               , ('T0168', '2024-01-11'), ('T0201', '2024-01-11'))
     --  assigns progressive numbering to the same DATA_MOV, ordering by TRANS_ID
   , W2 (TRANS_ID, ROW_NUM, DATE_MOV)
     as (select trim(W1.TRANS_ID)
              , row_number() over ( partition by DATE_MOV -- 'COMMON_VALUE'
                 order by W1.TRANS_ID) ROW_NUM
              , W1.DATE_MOV
          from W1 order by  W1.TRANS_ID)
select * from W2;

Risultato

Tabella originale (W1)

Tabella risultato (W2)

TRANS_ID DATE_MOV
T0011    2024-01-10
T0012    2024-01-10
T0013    2024-01-10
T0014    2024-01-10
T0015    2024-01-10
T0016    2024-01-10
T0017    2024-01-10
T0018    2024-01-10
T0019    2024-01-10
T0020    2024-01-10
T0023    2024-01-10
T0024    2024-01-10
T0025    2024-01-10
T0026    2024-01-10
T0127    2024-01-10
T0128    2024-01-10
T0129    2024-01-10
T0149    2024-01-10
T0150    2024-01-11
T0151    2024-01-11
T0152    2024-01-11
T0165    2024-01-11
T0166    2024-01-11
T0167    2024-01-11
T0168    2024-01-11
T0201    2024-01-11
TRANS_ID ROW_NUM  DATE_MOV
T0011          1  2024-01-10
T0012          2  2024-01-10
T0013          3  2024-01-10
T0014          4  2024-01-10
T0015          5  2024-01-10
T0016          6  2024-01-10
T0017          7  2024-01-10
T0018          8  2024-01-10
T0019          9  2024-01-10
T0020         10  2024-01-10
T0023         11  2024-01-10
T0024         12  2024-01-10
T0025         13  2024-01-10
T0026         14  2024-01-10
T0127         15  2024-01-10
T0128         16  2024-01-10
T0129         17  2024-01-10
T0149         18  2024-01-10
T0150          1  2024-01-11
T0151          2  2024-01-11
T0152          3  2024-01-11
T0165          4  2024-01-11
T0166          5  2024-01-11
T0167          6  2024-01-11
T0168          7  2024-01-11
T0201          8  2024-01-11

Descrizione dettagliata della query

La query SQL utilizza Common Table Expressions per creare un set di dati di esempio e poi assegnare un numero progressivo a ciascuna riga all’interno di gruppi basati sulla data del movimento (DATE_MOV), ordinando per l’ID della transazione (TRANS_ID).

La query è divisa in due parti principali:

W1: Definizione dei Dati di Partenza

La prima CTE, W1, definisce un set di dati di partenza con due colonne: TRANS_ID e DATE_MOV. Vengono inseriti manualmente dei valori per simulare i dati reali.

W2: Assegnazione del Numero Progressivo

La seconda CTE, W2, si basa sui dati definiti in W1. Utilizza la funzione ROW_NUMBER() per assegnare un numero progressivo a ciascuna transazione (TRANS_ID) all’interno di ogni data (DATE_MOV). La funzione ROW_NUMBER() è partizionata per DATE_MOV per garantire che la numerazione ricominci da 1 per ciascuna nuova data. Inoltre, l’ordine è stabilito dall’TRANS_ID per determinare l’ordine di assegnazione dei numeri all’interno di ogni gruppo di date.

Analizziamo più in dettaglio questa parte:

ROW_NUMBER() OVER (PARTITION BY DATE_MOV ORDER BY W1.TRANS_ID) AS ROW_NUM,

Questa espressione è un esempio di una funzione di finestra in SQL, nello specifico ROW_NUMBER(), che viene utilizzata per assegnare un numero progressivo unico a ogni riga all’interno di un set di risultati di una query. Andiamo a esaminare ogni componente di questa espressione:

ROW_ ROW_NUMBER()
è una funzione di finestra (window functions) che assegna un numero sequenziale a ogni riga in un set di risultati. Il numero inizia da 1 per la prima riga e aumenta di 1 per ogni riga successiva.

OVER (PARTITION BY DATE_MOV ORDER BY W1.TRANS_ID)
La clausola OVER specifica come la funzione di finestra ROW_NUMBER() deve essere applicata al set di risultati.

  • PARTITION BY DATE_MOV: Questo divide il set di risultati in partizioni (o gruppi) basati sul valore della colonna DATE_MOV. La funzione ROW_NUMBER() viene poi applicata all’interno di ciascuna di queste partizioni separatamente. Ciò significa che il conteggio ricomincia da 1 per ogni nuova data (DATE_MOV). In altre parole, ciascuna data avrà il proprio set di numeri progressivi.
  • ORDER BY W1.TRANS_ID: All’interno di ogni partizione (creata in base a DATE_MOV), le righe vengono ordinate in base al valore di TRANS_ID. La funzione ROW_NUMBER() assegna numeri progressivi a queste righe in base a quest’ordine. Quindi, per ogni data, la prima transazione nell’ordine avrà il numero 1, la seconda il numero 2, e così via.

AS ROW_NUM
Infine, AS ROW_NUM assegna un nome alla colonna che contiene il numero progressivo generato dalla funzione ROW_NUMBER(). In questo caso, la colonna sarà denominata ROW_NUM.

Tenere presente che l’implementazione e le funzionalità effettive potrebbero variare a seconda della versione del sistema operativo iSeries e del database DB2. È sempre buona norma fare riferimento alla documentazione ufficiale IBM per avere informazioni più precise e dettagliate


CTE Common Table Expression per individuare interruzioni nella sequenza numerica

Descrizione Contesto e Obiettivo

La query in questione è progettata per operare su una tabella che registra le transazioni, identificate da un Codice Movimento (MovCode) e associate a uno specifico identificativo (NumberID).

Mentre la tabella originale potrebbe contenere altre informazioni pertinenti, per lo scopo di questa query, l’attenzione è focalizzata esclusivamente su MovCode e NumberID.

L’obiettivo principale è generare un elenco in cui identificare e contrassegnare eventuali interruzioni nella sequenza numerica dei numeri identificativi (NumberID) per ogni Codice Movimento (MovCode).

Query

WITH xx (MovCode, NumberID)
        AS (VALUES ('A1002','101')
                  ,('A1002','102')
                  ,('A1002','104')
                  ,('A1002','105')
                  ,('A1002','108')
                  ,('A1002','109')
                  ,('A1002','110')
                  ,('B2002','201')
                  ,('B2002','202')
                  ,('B2002','203')
                  ,('B2002','204')
                  ,('B2002','210')
                  ,('B2002','211')
                  ,('B2002','214'))
   , CTE_CheckRegister (MovCode, NumberID, NextNumberID)
        AS (
              SELECT MovCode,
                     NumberID,
                     Lead(NumberID, 1) OVER (PARTITION BY MovCode ORDER BY NumberID) NextNumberID
              FROM xx
           )
SELECT   MovCode,
         CASE
            WHEN NextNumberID is NULL THEN NumberID
            WHEN (NumberID = NextNumberID - 1) THEN NumberID
            ELSE NumberID concat ' ***'
         END as NumberID
FROM     CTE_CheckRegister
ORDER BY MovCode, NumberID;

Risultato

Tabella originale ( XX )

Tabella risultato (CTE_CheckRegister)

MOVCODE	NUMBERID
A1002	101
A1002	102
A1002	104
A1002	105
A1002	108
A1002	109
A1002	110
B2002	201
B2002	202
B2002	203
B2002	204
B2002	210
B2002	211
B2002	214
MOVCODE	NUMBERID
A1002	101
A1002	102 ***
A1002	104
A1002	105 ***
A1002	108
A1002	109
A1002	110
B2002	201
B2002	202
B2002	203
B2002	204 ***
B2002	210
B2002	211 ***
B2002	214

Descrizione dettagliata della query

Questa query SQL utilizza le CTE (Common Table Expressions) e la funzione LEAD per identificare e contrassegnare eventuali interruzioni nella sequenza numerica dei numeri identificativi (NumberID) per ogni Codice Movimento (MovCode).

Ecco una spiegazione dettagliata di ciascuna parte della query:

  • CTE Iniziale xx: Questa CTE definisce un set di valori di esempio che rappresentano coppie di MovCode e NumberID. È una tabella temporanea utilizzata per dimostrare la logica della query.
  • CTE CTE_CheckRegister:
    • Utilizza i dati dalla CTE xx.
    • Applica la funzione LEAD per ottenere il numero del prossimo NumberID (NextNumberID) per ogni MovCode, ordinando per NumberID.
    • La funzione LEAD è una funzione di finestra che fornisce l’accesso a una riga a una certa distanza fisica dalla riga corrente, in questo caso, la riga successiva.
  • Selezione Finale e Logica CASE:
    • La query finale seleziona MovCode e una versione modificata di NumberID.
    • La logica CASE viene utilizzata per determinare cosa mostrare per NumberID:
      • Se NextNumberID è NULL, ciò indica che è l’ultimo numero identificativo per quel MovCode, quindi mostra semplicemente NumberID.
      • Se NumberID è consecutivo al NextNumberID (NumberID = NextNumberID – 1), mostra NumberID.
      • Altrimenti, se c’è un’interruzione nella sequenza (cioè, il numero NumberID non è seguito dal numero consecutivo), aggiunge ‘ ***’ a NumberID per segnalare l’interruzione.
  • Ordinamento dei Risultati:
    • I risultati sono ordinati per MovCode e NumberID, garantendo che l’output sia presentato in modo ordinato e sequenziale per ogni MovCode.

Tenere presente che l’implementazione e le funzionalità effettive potrebbero variare a seconda della versione del sistema operativo iSeries e del database DB2. È sempre buona norma fare riferimento alla documentazione ufficiale IBM per avere informazioni più precise e dettagliate

Articoli simili