venerdì 3 maggio 2013

Data Warehouse - Micro ETL Foundation - Tassonomia dei flussi di alimentazione

Introduzione

In un post precedente ho mostrato la complessità legata al caricamento di un flusso nel processo ETL. Nell'ultimo step si è data qualche informazione aggiuntiva relativa alla tipologia del flusso. 
In realtà in un progetto di Data Warehouse l'analisi dei flussi è una delle prime attività da affrontare. In questo post entreremo più in dettaglio in questo argomento cercando di evidenziare tutte le possibili informazioni legate ad un flusso. Come già ho avuto modo di affermare in altri miei articoli, la realtà dei fatti è molto più complessa della semplificazione grafica che troviamo nei documenti o nelle presentazioni. Il mio obiettivo è quello di porre la massima attenzione a questa complessità affinché i progettisti possano stimare correttamente i tempi di sviluppo di questa fase.
Scopriremo insieme quanti dettagli sono necessari per poter caricare correttamente un flusso
Vediamo subito come classificarne le informazioni e di definire tali informazioni come colonne di una tabella di configurazione dei flussi.
Una tabella di questo tipo non può mancare: è un metadato fondamentale del progetto. Iniziamo quindi con elencare le caratteristiche di un flusso di alimentazione.

  • Il nome del flusso
  • La locazione del flusso
  • La struttura del flusso
  • La data di riferimento dei dati all'interno del flusso
  • L' informazione di controllo del flusso

Il nome del flusso

Sembra banale, ma il nome del flusso è il primo problema che dobbiamo affrontare. In genere possiamo trovarci di fronte due situazioni diverse: nome del flusso fisso o nome del flusso variabile. 
Ci sono delle situazioni, legate essenzialmente al sistema alimentate, in cui il nome del flusso è fisso. Probabilmente il flusso viene generato ogni giorno e ogni giorno viene sovrascritto con lo stesso nome. Per esempio un flusso che si chiama clienti.txt verrà depositato in una cartella predefinita e sarà unico, cioè quel flusso appresenta l'anagrafica della clientela corrente aggiornata al giorno prima.
Un flusso con nome fisso e tipico delle situazioni in cui il flusso è preesistente al progetto di Data Warehouse e viene utilizzato anche da altre applicazioni.
Un flusso a nome variabile indica semplicemente che all'interno del nome del flusso è presente la data di generazione del flusso o altre informazioni più di dettaglio come per esempio ore minuti e secondi. Se possiamo essere noi a definire la nomenclatura del flusso, aggiungere l'informazione della data nel nome è senz'altro una buona soluzione. Evitiamo in questo modo che i flussi originali di alimentazione si sovrascivano, mantenendo così la storia delle alimentazioni.
Un altro esempio di nome variabile e quello in cui al nome è associato un numero sequenziale invece della data. Anche in questo caso non perdiamo mai il flusso precedente e abbiamo un maggior controllo della situazione. 

Quello che per esperienza posso consigliare è quello di definire entrambe le informazioni nel nome del flusso quindi, utilizzando l'esempio della clientela, si potrebbe definire il nome del flusso nel formato clienti_yyyymmdd_seq.txt. In questo modo se nella stessa giornata è necessario rigenerare più volte il flusso della clientela, siamo sicuri di tenere traccia della storia de i flussi della clientela.
Purtroppo, questa definizione richiede, da parte del sistema alimentate, uno sforzo costoso dal punto di vista lavorativo, per cui questa soluzione rimane spesso nell'ambito teorico.
La prima informazione che inseriremo nella nostra tabella di configurazione, sarà quindi una colonna che chiameremo file_like_txt in cui valorizzeremo il nome del flusso. Nel caso di flusso a nome fisso sarà, per esempio, 'clienti.txt'. Nel caso di nome a flusso variabile sarà per esempio,'clienti%.txt'. Indicando quindi con il simbolo di % la parte variabile del nome.

La locazione del flusso

Poiché siamo in un file system, il flusso deve essere presente in una cartella ben definita. Anche nel caso della locazione del flusso, non c'è una regola univoca, ma possiamo trovarci di fronte a situazioni diverse che ci obbligano a gestioni diverse. I casi più comuni sono tre. Vediamo nel dettaglio.

Il primo caso è il più semplice e, come al solito, il meno probabile. Il flusso viene inviato dal sistema alimentate in una cartella di ricezione del Data Warehouse. In questo caso non c'è molto da dire: ad una certa ora il flusso sarà presente sul server del database e sarà immediatamente processabile dal processo ETL. Tale processo, una volta elaborato il flusso lo sposterà dalla cartella locale a una cartella di salvataggio, predisponendo quindi la cartella di ricezione per l'arrivo di un nuovo flusso Quindi flusso locale.

Anche il secondo caso è abbastanza semplice. Il flusso non sarà presente localmente ma sarà posizionato dal sistema alimentate in una cartella di rete visibile (mappata) dal server. In questo caso sarà il processo ETL a copiare il flusso dalla cartella di rete alla cartella di ricezione. Il flusso non può essere spostato dalla cartella di rete perché probabilmente deve essere utilizzato anche da altri sistemi. Quindi una gestione leggermente diversa da quella precedente.

Il terzo caso è un po' più complesso. Il flusso è presente su un server raggiungibile solo mediante ftp. Questo significa che il processo ETL deve eseguire una ftp get del flusso per posizionarlo nella cartella di ricezione.
La realtà può presentare anche casi più complessi di locazione del flusso (per esempio via e-mail) ma i tre evidenziati sono sicuramente i più comuni.
 

Nella tabella di configurazione sarà pertanto necessario avere le seguenti colonne:
rcv_dir = in cui valorizziamo il path della cartella di ricezione del flusso (nel caso di Oracle il nome della Oracle directory)
src_dir = in cui valorizziamo il path della cartella di rete  (nel caso di Oracle il nome della Oracle directory). Valorizzata solo per il caso due.
bck_dir = in cui valorizziamo il path della cartella di salvataggio storico del flusso  (nel caso di Oracle il nome della Oracle directory).
host_txt, usrpwd_txt, port_num = in cui valorizziamo i riferimenti relativi al server ftp, username e password e la porta di connessione. Valorizzati solo per il caso tre.

La struttura del flusso

Analizziamo ora la struttura del flusso. Per la struttura del flusso intendiamo sia la sua composizione generale sia il modo con cui le informazioni sono organizzate al suo interno. Come al solito possiamo trovarci di fronte a strutture di vario tipo, ma ci focalizzeremo sulle due strutture più comuni, cioè flussi con campi a lunghezza fissa e flussi con campi separati da qualche simbolo di punteggiatura. Non considereremo quindi strutture più complesse come file pdf, xml o altro.
I flussi con campi a lunghezza fissa sono tipici dei sistemi alimentanti in cui la produzione del flusso è eseguita, nell’host, da programmi cobol o linguaggi simili. In questi casi è necessario avere la documentazione della lunghezza esatta dei vari campi che compongono il flusso, ricordando che il flusso è, sempre e comunque, un semplice file di testo.
I flussi con separatore sono quelli più semplici, ovviamente, occupano meno spazio e tipicamente hanno una estensione universalmente conosciuta come .csv.
Per quanto riguarda la composizione generale, possiamo avere flussi con una o più righe di testata e una o più righe di coda.
 

Le informazioni necessarie alla tabella di configurazione per descrivere la struttura del flusso saranno le seguenti

sep_txt = in cui valorizziamo il simbolo di separazione dei campi, per esempio ';' o ','. 


head_cnt = in cui valorizziamo il numero di righe di testata


foo_txt = in cui valorizziamo il numero di righe di coda.


A questo punto è opportuno introdurre una nuova tabella di configurazione. Questa tabella è necessaria per descrivere i singoli campi che compongono il flusso. Non servono molte informazioni e ci limiteremo a quelle essenziali. Ovviamente, più è ricca, più metadati avremo a disposizione.


column_id = identificatore numerico della colonna. È un semplice numero sequenziale che indica l'ordine della colonna all'interno della riga. 

column_cod = nome della colonna

colsize_num = dimensione della colonna in byte. Nel caso di campi a lunghezza fissa indica il numero di caratteri occupati. Nel caso di campi con terminatore deve indicare la massima lunghezza possibile della colonna.


fxv_txt = questa informazione può essere utile nel caso in cui sia necessario effettuare delle semplici trasformazioni sul campo di input: per esempio delle conversioni da carattere numero o da formato di una data a un altro.

La data di riferimento

Affrontiamo ora il discorso della data di riferimento. Comprendere bene cosa significa “data di riferimento” è fondamentale per l'intero progetto. Può sembrare un'esagerazione ma non è così. 
Sulla data di riferimento devono essere basate le politiche di versionamento delle dimensioni di analisi (slowly changing dimension). 
Sulla data di riferimento vengono partizionate le tabelle dei fatti. Un errore in fase iniziale può costare molto caro nelle fasi successive del progetto.
In realtà parlare di data di riferimento è un po' generico: distinguiamo due tipi di date di riferimento: la data di riferimento del dato e la data di riferimento del flusso. La differenza fra le due date la si può mostrare con un esempio.
 

Supponiamo di ricevere mensilmente un flusso di movimenti (conti correnti, titoli o altro) che contiene tutti i movimenti giornalieri del mese. La differenza fra le due date è chiara. La data di riferimento del flusso sarà, per esempio, l'ultimo giorno del mese. Per cui il flusso si chiamerà per esempio mov_20130831.txt. Mentre la data di riferimento dei dati saranno i singoli giorni del mese memorizzati in una colonna del flusso.
Ovviamente ciò che è importante è la data di riferimento dei dati, chiamiamola DAY_KEY, perché è su di essa che si baseranno le dimensioni e i fatti dei data mart finali di analisi. Consiglio che tale data sia sempre nel formato yyyymmdd (così come tutte le date presenti nelle tabelle del Data Warehouse).
 

Il problema sorge quando il flusso non contiene una colonna data. Se siamo noi a definire il tracciati dei flussi, il mio consiglio è quello di avere sempre in ogni flusso la DAY_KEY. Ma se i flussi sono preesistenti e non modificabili, senza alcuna data di riferimento, allora dobbiamo trovare il modo di associarne una ai dati. 
In questo caso le situazioni più comuni sono tre.
  • La data di riferimento dei dati è presente in una riga di testata del flusso.
  • La data di riferimento dei dati è presente nel nome del flusso.
  • La data di riferimento dei dati non è presente né in testata, nè nel nome.
Per poter gestire queste tre situazioni vediamo ora quali informazioni sono necessarie nella tabella di configurazione.
Nel primo caso vediamo comodamente anche in figura quello di cui abbiamo bisogno

 



idr_num (Internal Data Row) = con questo numero indichiamo l'offset di riga,a partire dall'inizio del flusso, in cui troviamo la data di riferimento. Nel caso di esempio l'offset sarà 0.

idc_num (Internal Data Column) = con questo numero indichiamo, nella riga precedente, la colonna di partenza della data di riferimento. Nel caso di esempio sarà 9.
 

idf_txt (Internal data Format) = con questa stringa indichiamo il formato della data di riferimento. Nel caso di esempio sarà ddmmyyyy.

ido_num (Internal data offset) = con questo numero indichiamo l'offset in giorni della data di riferimento. Questa informazione è utile nel caso in cui la generazione del flusso e la sua elaborazione avvengono in giorni differenti.
Facciamo un esempio. Oggi è il giorno 14 e parte l'elaborazione del flusso; in testata troviamo 13 e assumiamo che quello è il giorno di riferimento dei dati. In questo caso ido_num è uguale a zero e la nostra assunzione è corretta. Supponiamo ora che la generazione del flusso dati avvenga dopo la mezzanotte per cui in testata troviamo 14. In questo caso ido_num deve essere impostato a -1, perché anche se l'elaborazione è avvenuta dopo la mezzanotte sicuramente i dati anagrafici fanno riferimento alla situazione del giorno 13. Questa, in sintesi, è la necessità di questo campo.
 

Nel secondo caso la data è presente nel nome del flusso per cui, come nel caso della testata, dobbiamo definire le coordinate che ci permettano di identificare quella data. I campi di configurazione perciò saranno simili al caso uno ma li identificheremo con una ‘e’ iniziale (external). Supponendo un flusso di nome ana_07-01-2013.txt, imposteremo i seguenti campi

edc_num (External Data Column) = 5


edf_txt (External data Format) = 'dd-mm-yyyy'


edo_num (External data offset) = 0

Nel terzo caso assumeremo di prendere come riferimento la data di sistema, per cui sarà necessario soltanto un campo di configurazione. Se la nostra elaborazione parte la mattina sarà:


sdo_num (Sytem data offset) = -1

L' informazione di controllo

Giungiamo finalmente all'ultima caratteristica di un flusso di alimentazione. L' informazioni di controllo. Per informazione di controllo intendiamo il numero di righe che compongono il flusso di alimentazione. 
Questa informazione non è obbligatoria e spesso non è presente. Il mio consiglio è quello, se è possibile, di prenderla sempre in considerazione anche se questo significa complicare il processo, sia del sistema alimentatnte, che di quello ETL.
Avere il numero di righe del flusso dallo stesso sistema che lo produce, è l'unico modo per garantirci che non ci sono stati problemi nel trasferimento del flusso. Posso affermare per esperienza che la situazione in cui un flusso per qualche motivo non arrivi completo non è così rara, e avere il numero di righe da controllare, è l'unico strumento che ci garantisca l'integrità del flusso. 

Le modalità con cui può pervenire questa informazione di controllo sono essenzialmente due.
La prima, molto comune, è quella di avere il numero di righe come informazione finale del flusso stesso. La seconda modalità e quella di ricevere un flusso specifico  composto da un'unica riga che contiene il numero di righe del flusso dati.
Per gestire queste due possibilità, le informazioni da inserire nella tabella di configurazione saranno le seguenti:


rcr_num (Row Counter Row) = indica l’offset di righe a partire dal fondo del flusso. In pratica se il contatore di righe è l'ultima riga del flusso dovrà essere impostato a zero.


rcc_num (Row Counter Column) = indica in quale posizione di colonna inizia il contatore di righe.


rcs_num (Row Counter Size) = indica quanti caratteri occupa il numero di righe.


rcf_like_txt = questo campo sarà valorizzato solo nel caso in cui è presente il flusso separato. Si assume che il nome del flusso abbia sempre come prefisso il nome del flusso dati e termini con un suffisso specifico in questo campo. 


Per esempio supponiamo che il flusso dati si chiami mov_20130831.txt. Se in questo campo valorizziamo ‘.row’ significa che il flusso separato si chiamerà mov_20130831.row. Se in questo campo valorizziamo ‘_row.txt’ significa che il flusso separato si chiamerà mov_20130831_row.txt. L'obiettivo di questa regola è fare in modo di desumere immediatamente il nome del flusso separato sostituendo, nel nome del flusso dati, tutto quello che c'è dopo il punto (compreso) con quanto valorizzato in questo campo.

Conclusione

Ho cercato di analizzare in dettaglio le principali caratteristiche dei flussi di alimentazione di un Data Warehouse. Inoltre abbiamo definito due tabelle di configurazione in cui si è cercato di rappresentare tutte le informazioni utili a una gestione automatica.
Quanto descritto non può essere esaustivo ma probabilmente copre l'80% della casistica reale. Riassumiamo in un semplice grafico le caratteristiche esposte.