Tabella divisa in QVD per periodo con ricarica incrementale

| | Comments (0)

Scenario:

  • tabella di milioni di righe, dove si può assumere che non ci siano modifiche/eliminazioni a ritroso
  • data warehouse in cui si voglia creare un QVD per periodo

Succede abbastanza di frequente, soprattutto con tabelle che si limitano a loggare eventi.
Assumendo che la lunghezza del periodo del singolo QVD sia un mese, ecco quel che si può ragionevolmente voler ottenere:

  • evitare di ricaricare i dati già caricati
  • evitare l'intervento manuale: deve capire da solo cosa ricaricare
  • se un periodo dev'essere ricaricato, dev'essere sufficiente cancellare il qvd

Insomma, la scaletta delle cose da fare sarebbe la seguente:

  1. caricare la lista di periodi esistenti sul database
  2. per ogni periodo, eccetto quello odierno:
    1. se c'è già il QVD, saltarlo a piè pari
    2. se non c'è già il QVD, caricare e salvarlo
  3. per il periodo odierno, salvare e sovrascrivere il qvd

Faccio un esempio con Oracle come database:

// i .qvd saranno del tipo Tabella200802.qvd,
// oppure TabellaMeseOdierno.qvd
set prefissoQvd=Tabella;

trace Carico la lista di tutti i mesi...;
tmpAnniMese:
load
	AAAAMM as annoMese
;
SQL SELECT DISTINCT TO_CHAR(DATA, 'YYYYMM') AAAAMM FROM TABELLA;

let iMax=noofrows('tmpAnniMese')-1;
let meseOggi=peek('annoMese', -1, 'tmpAnniMese');

for i=0 to $(iMax);
	let mese=peek('annoMese', $(i), 'tmpAnniMese');
	let righeFile='0' & qvdnoofrecords('$(prefissoQvd)$(mese).qvd');
	
	if ($(righeFile) = 0) then
		trace Carico $(mese)...;
		AnagJn:
		load ...;
		SQL SELECT ...	FROM TABELLA WHERE TO_CHAR(DATA, 'YYYYMM') = '$(mese)';

		trace Salvo $(mese) su QVD...;
		
		// verifico se è il mese odierno, per scegliere il nome del file
		if ('$(mese)' = '$(meseOggi)') then
			store AnagJn into $(prefissoQvd)MeseOdierno.qvd;
		else
			store AnagJn into $(prefissoQvd)$(mese).qvd;
		end if
		drop table AnagJn;
	else
		trace Salto $(mese)!;
	end if
next i

drop table tmpAnniMese;

Essenzialmente, carica in una tabella temporanea tutti i valori distinti di anno-mese, e poi per ogni riga di quella tabella va a verificare se il file non esiste ancora (o se è il mese corrente), se sì lo carica, altrimenti passa al successivo.
Così facendo i tempi diminuiscono sensibilmente rispetto a caricare tutto ogni volta, nell'ordine di decine di volte, tanto più quanto grande è la tabella.

Una nota di contorno: se sulla tabella c'è un indice sul campo DATA, le due SELECT del codice di esempio non ne fanno uso poiché usano una trasformazione del campo stesso, costringendo il DBMS a leggere tutti i valori, scorrendo tutta la tabella.
Per sfruttare al massimo gli indici, si può sostituire la prima SELECT (che prende tutti i valori distinti di anno-mese con la seguente, che ritornerà tutti i valori pressoché istantaneamente (strettamente legata alla sintassi Oracle):

SELECT TO_CHAR(START_DAY, 'YYYYMM') AAAAMM
	FROM TABLE(CALENDAR.MONTHS((SELECT MIN(DATA) FROM TABELLA),
		(SELECT MAX(DATA) FROM TABELLA)));

La clausola WHERE della seconda, invece, si può sostituire con la seguente:

WHERE DATA BETWEEN TO_DATE('$(mese)', 'YYYYMM') AND
	TO_DATE(TO_CHAR(LAST_DAY(TO_DATE('$(mese)', 'YYYYMM')),
		'YYYYMMDD') || '235959', 'YYYYMMDDHH24MISS')

Leave a comment

Novità! Se vuoi essere contattato da un consulente Querit lascia nome e numero e Ti telefoneremo:





Pages