versione italiana versione italiana
english version english version

Misure e dimensioni non correlate

In un cubo virtuale si possono avere misure e dimensioni provenienti da cubi diversi. Questo consente di fare analisi comparative tra dati provenienti da tabelle dei fatti diverse, come il classico caso delle vendite rapportate agli acquisti.

Talvolta, il fatto di non poter esplodere una misura su tutte le dimensioni è considerato una limitazione, anche se in realtà il dato reale non esiste. Per esempio, immaginiamo di avere questi due cubi di partenza:

  • Cubo Acquisti
    Misura: Costi
    Dimensioni: Prodotto, Fornitore, Tempo
  • Cubo Vendite
    Misura: Vendite
    Dimensioni: Prodotto, Cliente, Tempo

Possiamo creare un cubo virtuale come quello che segue:

  • Cubo AcquistiVendite
    Misura: Costi, Vendite
    Dimensioni: Prodotto, Fornitore, Cliente, Tempo

Il cubo AcquistiVendite ottenuto è navigabile su 4 dimensioni, ma in realtà alcune misure sono visibili limitatamente: i Costi non sono visualizzati sul dettaglio dei Clienti, mentre le Vendite non sono visualizzate sul dettaglio dei Fornitori.

In un sistema perfetto, dove tutti i lotti acquistati sono tracciati dall’inizio alla fine, potremmo associare le vendite ai fornitori e gli acquisti ai clienti, per ogni prodotto movimentato; spesso però questa informazione non è disponibile e se lo fosse avremmo anche la dimensione Cliente nel cubo Acquisti e la dimensione Fornitore nel cubo Vendite: in tal caso il problema sarebbe risolto già alla radice.

Nel mondo reale, invece, lo scenario iniziale è quello più frequente. Alcune aziende definiscono degli algoritmi interni per ripartire, a posteriori e con un certo margine d’errore, il valore dei Costi sui Clienti (il caso di ripartizione delle Vendite sui Fornitori è analogo, per cui ci occupiamo solo di uno). Tale algoritmo può essere più o meno sofisticato, prendendo in considerazione informazioni le informazioni più disparate, ma essenzialmente si tratta di una formula che deve ripartire il valore totale delle vendite per tutti i Clienti su ogni singolo cliente. Affrontiamo il caso più semplice, in cui la ripartizione avviene in maniera proporzionale al fatturato. In pratica avremo:

 

[Costo Cliente] = [Costo Tot. Clienti] * ( [Fatturato Cliente] /[Fatturato Tot. Clienti] )

 

Per implementare questo calcolo nel cubo ci sono due strategie:

  • Membri calcolati: creando un membro calcolato nella dimensione Measures è possibile definire una logica condizionale tale che l’algoritmo sia applicato solo in corrispondenza degli incroci con i livelli delle dimensioni che richiedono il calcolo (nel nostro esempio, tutti i livelli della dimensione Clienti che non siano il livello [All Clienti]).
  • Celle calcolate: si può anche definire una cella calcolata, definendo il subcube di applicazione e una formula adeguata. La cella calcolata propaga i suoi effetti anche sulle aggregazioni sovrastanti, quindi bisogna adottare cautela sia per ottenere dei valori corretti (recuperando i valori originali per le celle che non necessitano del calcolo, senza che si applichi l’aggregazione dei valori calcolati ai livelli più bassi), sia per ottenere prestazioni adeguate (il calcolo delle aggregazioni viene spostato a run-time e può essere molto pesante su un cubo di dimensioni medio-grandi).

Vediamo quindi come implementare il nostro algoritmo di calcolo con un membro calcolato nella dimensione Measures; la formula sarà concettualmente analoga alla seguente:

 
IIF( [Clienti].CurrentMember.Level.Ordinal = 0,
-- Non applica calcoli, recupera il dato originario
[Measures].[Costi],
-- Ripartisce il costo di tutti i clienti proporzionalmente
-- al fatturato del singolo cliente
([Measures].[Costi], [Clienti].[All Clienti]) *
( ([Measures].[Vendite], [Clienti].CurrentMember )
/ ([Measures].[Vendite], [Clienti].[All Clienti] ) ) )

Vale la pena commentare l’uso di IIF: il controllo del livello del membro corrente della dimensione Clienti consente di stabilire se debba essere applicato l’algoritmo di ripartizione (livello diverso da 0) oppure no (livello uguale a 0). Il valore della misura Costi è presente nel livello [All Clienti], perché si tratta del valore definito nel cubo Acquisti dove la dimensione Clienti non è definita. Quando ci si trova a un livello diverso, è necessario applicare l’algoritmo di ripartizione: in questa parte dell’espressione MDX possiamo notare come i valori siano ottenuti attraverso tuple che specificano le coordinate sulla dimensione Measures e sulla dimensione Clienti. Se non si specificasse la dimensione Measures, infatti, il membro corrente sarebbe il nostro membro calcolato e otterremmo un errore di valutazione. Per il resto, l’espressione MDX non fa altro che implementare l’algoritmo che abbiamo definito in precedenza.

In un caso reale è possibile che l’algoritmo di ripartizione sia più complesso, prendendo in esame dati presenti nelle proprietà del cliente o del prodotto, piuttosto che informazioni acquisite da altri cubi con la funzione MDX LookupCube; ciò non toglie che la logica di implementazione sia basata su questo stesso costrutto, in cui si verifica prima di tutto se si sta calcolando il valore di una cella disponibile (dimensioni presenti nel cubo in cui ha origine la misura che si vuole visualizzare) e si applica l’algoritmo solo nei casi in cui si stiano interrogando dimensioni che non sono presenti nel cubo originario. La complessità dell’algoritmo aumenta anche all’aumentare delle dimensioni “mancanti” nel cubo da cui proviene la misura che si intende visualizzare (la logica di ripartizione dovrà prevedere molti più casi).

Tipicamente, quando si applica la tecnica descritta, il cubo virtuale “nasconde” la misura originale proveniente dal cubo reale e la sostituisce col membro calcolato, evitando ambiguità per l’utente.

 

Per completezza è utile citare che l’espressione precedente è semplificabile in due punti: la lettura di Costi è ottenibile con l’uso della funziona ValidMeasure, che rende inutile specificare i membri All* per le dimensioni su cui la misura stessa non è disponibile (in questo caso solo Clienti); la lettura di Vendite rispetto al cliente corrente può essere effettuata omettendo il CurrentMember dalla tuple (che prima era presente per semplificare la lettura). La formula precedente è quindi riscrivibile in questa forma abbreviata (in grassetto le parti modificate):

 
IIF( [Clienti].CurrentMember.Level.Ordinal = 0,
-- Non applica calcoli, recupera il dato originario
[Measures].[Costi],
-- Ripartisce il costo di tutti i clienti proporzionalmente
-- al fatturato del singolo cliente
ValidMeasure([Measures].[Costi]) *
( [Measures].[Vendite]
/ ([Measures].[Vendite], [Clienti].[All Clienti] ) ) )

Notare che questa formula richiederebbe comunque una modifica nel caso in cui si aggiunga una dimensione al cubo contenente la misura Vendite riportandola anche nel cubo virtuale: tale dimensione andrebbe aggiunta nella tuple al denominatore della divisione nella formula appena vista. Ovvio che l’algoritmo reale di calcolo dovrà rispecchiare una logica di business consistente, mentre quello presentato è un esempio un po’ teorico che difficilmente trova un riscontro pratico.