ESEMPIO A: Arco multiplo su LIBRO-‐AUTORE

Transcript

ESEMPIO A: Arco multiplo su LIBRO-‐AUTORE
ESEMPIO A: Arco multiplo su LIBRO-­‐AUTORE Consideriamo un DBO con il seguente schema E/R ed il corrispondente schema relazionale: SCONTRINO
ANNO
AUTORE(AUTORE,CITTA)
(1,N)
LIBRO(LIBRO,GENERE)
IDSCONTRINO
DI
(1,1)
AUTORE
CITTA
DETTAGLIO
VENDITE
NUMERO
PROG
PESO(AUTORE:AUTORE,
LIBRO:LIBRO,PESO)
PREZZO
SCONTRINO(IDSCONTRINO,ANNO)
(1,1)
AUTORE
DETTAGLIO_VENDITE(
IDSCONTRINO:SCONTRINO,NUMEROPROG,
LIBRO:LIBRO,
PREZZO)
DEL
(1,N)
LIBRO
(1,N)
LIBRO
GENERE
PESO
(1,N)
PESO
Viene richiesto di A) Progettazione concettuale : Fatto VENDITE con dimensioni {SCONTRINO,NP,LIBRO} e misure {INCASSO,NUMERO}, considerando l’arco multiplo su LIBRO-­‐AUTORE. Glossario delle Misure NUMERO Inteso come numero complessivo delle vendite, è una misura di impatto, INCASSO: Inteso come incasso totale, è una misura pesata, B) Progettazione logica : STAR-­‐SCHEMA con PUSH-­‐DOWN C) Alimentazione : Scrivere in SQL l’alimentazione della fact-­‐table. D) SQL-­‐OLAP : Scrivere e discutere in SQL-­‐OLAP il pattern {AUTORE,ANNO} e sub-­‐pattern. Scrivere e discutere in SQL-­‐OLAP il pattern {LIBRO,ANNO} e sub-­‐pattern. 1 Istanza del DBO Eventi Primari del Fatto VENDITE con dimensioni SCONTRINO, NP, LIBRO Pattern {AUTORE,ANNO} e sub-­‐pattern
2 ANNO
SCONTRINO
CITTA
LIBRO
VENDITE
NP
NUMERO
INCASSO
AUTORE
GENERE
Lo schema di fatto è TRANSAZIONALE. Dipendenza Funzionale tra le dimensioni {SCONTRINO,NP} à LIBRO Misure
INCASSO: misura normale additiva; definita come INCASSO=PREZZO; misura pesata NUMERO: misura calcolata come NUMERO= COUNT(*); misura di impatto Progettazione Logica : Soluzione con Push-down
Prima di discutere la soluzione, consideriamo il caso senza arco multiplo :
FACT_TABLE(SCONTRINO:DT_SCONTRINO,NP,LIBRO:DT_LIBRO,INCASSO)
NUMERO: misura calcolata come NUMERO= COUNT(*) SELECT LIBRO, LIBRO,NP,SCONTRINO
NUMERO = COUNT(*)
FROM FACT_TABLE
GROUP BY LIBRO,NP,SCONTRINO WITH CUBE
NUMERO si può calcolare in forma equivalente attraverso NUMERO=SUM(NUMERO) FACT_TABLE(SCONTRINO:DT_SCONTRINO,NP,LIBRO:DT_LIBRO,INCASSO,NUMERO)
con NUMERO=1 per tutte le istanze di FACT_TABLE; quindi SELECT LIBRO, LIBRO,NP,SCONTRINO
NUMERO = SUM(NUMERO)
FROM FACT_TABLE
GROUP BY LIBRO,NP,SCONTRINO WITH CUBE
3 Con Arco Multiplo AM: Soluzione con Push-down
Lo schema di tale soluzione è il seguente MISURA M PATTERN SENZA ATTRIBUTI gerarchia AM CON ATTRIBUTI gerarchia AM Valore Pesato M_P IMPATTO Valore non Pesato X X PESATA X X Valore Pesato M_P NUMERO à impatto INCASSO à pesata FACT_TABLE_PD(SCONTRINO:DT_SCONTRINO,NP,LIBRO:DT_LIBRO,
INCASSO_PESATO,NUMERO_PESATO,NUMERO)
Dove NUMERO = 1 NUMERO_PESATO = NUMERO * PESO = PESO INCASSO_PESATO = INCASSO * PESO FACT_TABLE normale : CREATE VIEW FACT_TABLE
AS SELECT
IDSCONTRINO AS SCONTRINO,
NUMEROPROG AS NP, LIBRO,
PREZZO AS INCASSO
FROM DETTAGLIO_VENDITE
4 FACT_TABLE con push-­‐down: CREATE VIEW FACT_TABLE_PD
AS SELECT
SCONTRINO,NP,P.LIBRO,
AUTORE,
INCASSO_PESATO=INCASSO*PESO,
NUMERO = 1,
NUMERO_PESATO=PESO
FROM FACT_TABLE E JOIN
PESO P ON
(E.LIBRO=P.LIBRO) Il pattern {AUTORE,ANNO} e sub-­‐pattern si ottengono come SELECT AUTORE,ANNO, SUM(INCASSO_PESATO) AS INCASSO, NUMERO= CASE WHEN GROUPING(AUTORE)=1 THEN SUM(NUMERO_PESATO) ELSE SUM(NUMERO) END FROM FACT_TABLE_PD FT JOIN DT_SCONTRINO S ON S.SCONTRINO = FT.SCONTRINO GROUP BY AUTORE,ANNO WITH CUBE NOTA: NUMERO si può calcolare in forma equivalente attraverso NUMERO= COUNT(*) FACT_TABLE_PD(SCONTRINO:DT_SCONTRINO,NP,LIBRO:DT_LIBRO,
INCASSO_PESATO,NUMERO_PESATO,NUMERO)
Dove NUMERO = 1 NUMERO_PESATO = NUMERO * PESO INCASSO_PESATO = INCASSO * PESO 5 FACT_TABLE con push-­‐down: CREATE VIEW FACT_TABLE_PD
AS SELECT
SCONTRINO,NP,P.LIBRO,
AUTORE,
INCASSO_PESATO=INCASSO*PESO,
NUMERO = 1,
NUMERO_PESATO=PESO
FROM FACT_TABLE E JOIN
PESO P ON
(E.LIBRO=P.LIBRO) Il pattern {AUTORE,ANNO} e sub-­‐pattern in questo caso si ottengono come SELECT AUTORE,ANNO, SUM(INCASSO_PESATO) AS INCASSO, NUMERO= CASE WHEN GROUPING(AUTORE)=1 THEN SUM(NUMERO_PESATO) ELSE SUM(NUMERO) COUNT(*) END FROM FACT_TABLE_PD FT JOIN DT_SCONTRINO S ON S.SCONTRINO = FT.SCONTRINO GROUP BY AUTORE,ANNO WITH CUBE 6 Variante : aggiungere al precedente schema di fatto la misura INCASSO_MEDIO, calcolata come INCASSO/NUMERO (in altre parole è l’incasso medio calcolato considerando come incasso per un certo autore i suoi libri con il relativo peso , e come numero di libri venduti il numero senza peso) Il risultato che si vuole ottenere, ad esempio per {AUTORE,ANNO} e sub-­‐pattern, è mostrato di seguito: Il calcolo di INCASSO_MEDIO si può aggiungere alla precedente query; siccome nel calcolo c’è una misura di impatto (NUMERO) si deve differenziare SELECT AUTORE,ANNO, SUM(INCASSO_PESATO) AS INCASSO, NUMERO= CASE WHEN GROUPING(AUTORE)=1 THEN SUM(NUMERO_PESATO) ELSE SUM(NUMERO) END, INCASSO_MEDIO = CASE WHEN GROUPING(AUTORE)=1 THEN SUM(INCASSO_PESATO)/SUM(NUMERO_PESATO) ELSE SUM(INCASSO_PESATO)/SUM(NUMERO) END FROM FACT_TABLE_PD FT JOIN DT_SCONTRINO S ON S.SCONTRINO = FT.SCONTRINO GROUP BY AUTORE,ANNO WITH CUBE Oppure si definisce la precedente query (quella senza INCASSO_MEDIO) come una vista, diciamo AUTORE_ANNO e quindi si calcola INCASSO_MEDIO come INCASSO/NUMERO SELECT AUTORE_ANNO.*, INCASSO_MEDIO = INCASSO/NUMERO FROM AUTORE_ANNO Variante : aggiungere al precedente schema di fatto la misura 7 INCASSO_MEDIO, misura pesata, è l’incasso medio calcolato considerando come incasso per un certo autore i suoi libri con il relativo peso , e come numero di libri venduti il numero con il relativo peso Il risultato che si vuole ottenere, ad esempio per {AUTORE,ANNO} e sub-­‐pattern, è mostrato di seguito: Pattern {AUTORE,ANNO} e sub-­‐pattern
Nella soluzione con push-­‐down, una misura pesata aggregata tramite AVG deve essere definita come à misura calcolata come SUM(INCASSO_PESATO) / SUM(NUMERO_PESATO) quindi INCASSO_MEDIO può essere calcotato con le misure già presenti nella FACT_TABLE_PD, semplicemende applicando la precedente espressione Backup del DBO : http://www.dbgroup.unimo.it/SIA/ArcoMultiploLIbro.bak Per provare la soluzione di questo Esercizio Per le query SQL-­‐OLAP occorre avere tutto lo schema logico e non solo la FACT_TABLE, cioè occorre avere anche DT_SCONTRINO(SCONTRINO,ANNO)
DT_LIBRO(LIBRO,GENERE)
DT_AUTORE(AUTORE,CITTA) Quindi occorre prima crearsi le relative view, che in questo caso sono banali, ad esempio: CREATE VIEW DT_SCONTRINO AS
SELECT IDSCONTRINO AS SCONTRINO,ANNO
FROM SCONTRINO
8 ESEMPIO B: Arco multiplo su LIBRO-­‐AUTORE Consideriamo un DBO con il seguente schema E/R ed il corrispondente schema relazionale: SCONTRINO
ANNO
AUTORE(AUTORE,CITTA)
(1,N)
LIBRO(LIBRO,GENERE)
IDSCONTRINO
DI
(1,1)
AUTORE
CITTA
DETTAGLIO
VENDITE
NUMERO
PROG
PESO(AUTORE:AUTORE,
LIBRO:LIBRO,PESO)
PREZZO
SCONTRINO(IDSCONTRINO,ANNO)
(1,1)
AUTORE
DETTAGLIO_VENDITE(
IDSCONTRINO:SCONTRINO,NUMEROPROG,
LIBRO:LIBRO,
PREZZO)
DEL
(1,N)
LIBRO
(1,N)
LIBRO
GENERE
PESO
(1,N)
PESO
Viene richiesto di A) Progettazione concettuale : Fatto VENDITE con dimensioni { LIBRO,ANNO} e misure {INCASSO,NUMERO,NUMEROCLIENTI}, considerando l’arco multiplo su LIBRO-­‐AUTORE. Glossario delle Misure NUMERO: Inteso come numero delle vendite, è una misura di impatto, INCASSO: Inteso come incasso totale, è una misura pesata, NUMEROCLIENTI Inteso come numero di scontrini, è una misura di impatto. In uno scontrino posso avere più volte lo stesso libro: tramite NUMEROCLIENTI si vuole contare questa vendita solo una volta B) Progettazione logica : STAR-­‐SCHEMA con PUSH-­‐DOWN C) Alimentazione : Scrivere in SQL l’alimentazione della fact-­‐table. D) SQL-­‐OLAP : Scrivere e discutere in SQL-­‐OLAP il pattern {AUTORE,LIBRO } e sub-­‐pattern. 9 Istanza del DBO Eventi Primari del Fatto VENDITE con dimensioni LIBRO, ANNO Pattern {LIBRO,ANNO} e sub-­‐pattern 10 Soluzione
Progettazione Concettuale
CITTA
LIBRO
AUTORE
VENDITE
ANNO
NUMERO
INCASSO
NUMEROCLIENTI
GENERE
Temporale. Nessuna Dipendenza Funzionale tra le dimensioni Misure
NUMERO: misura normale additiva, definita come NUMERO =COUNT(*) ; misura di impatto, INCASSO: misura normale additiva, definita come INCASSO =SUM(ESAME.CFU) ; misura pesata NUMEROCLIENTI: misura normale additiva, definita come NUMEROCLIENTI =COUNT(DISTINCT IDSCONTRINO) ; misura di impatto, E’ facile verificare che mentre INCASSO e NUMERO sono additive rispetto ad entrambe le dimensioni, la misura NUMEROCLIENTI è non aggregabile rispetto alla dimensione LIBRO mentre è addittiva rispetto alla dimensione ANNO (in quanto IDSCONTRINO à ANNO). Progettazione Logica : Soluzione con Push-Down
Nella progettazione logica con push-­‐down, si deve considerare ciascuna misura ed in base alla tipologia decidere cosa riportare nella FACT_TABLE_PD NUMERO: misura normale additiva ; misura di impatto In FACT_TABLE_PD viene riportato il valore pesato (NUMERO_P) e non pesato (NUMERO) INCASSO: misura normale additiva ; misura pesata In FACT_TABLE_PD viene riportato il valore pesato (INCASSO_P) NUMEROCLIENTI: misura normale additiva ; misura di impatto In FACT_TABLE_PD viene riportato il valore pesato (NUMEROCLIENTI _P) e non pesato (NUMEROCLIENTI) Quindi lo star schema risulta essere FACT_TABLE_PD(ANNO, LIBRO:DT_LIBRO, AUTORE: DT_AUTORE
NUMERO_P, NUMERO_P, NUMERO_P, NUMEROCLIENTI _P, NUMEROCLIENTI)
DT_LIBRO(LIBRO,GENERE)
DT_AUTORE(AUTORE,CITTA)
11 Alimentazione
FACT_TABLE normale: CREATE VIEW FACT_TABLE AS
SELECT
ANNO, LIBRO,
SUM(PREZZO) AS INCASSO,
COUNT(*) AS NUMERO,
COUNT(DISTINCT SCONTRINO.IDSCONTRINO) AS
NUMEROCLIENTI
FROM DETTAGLIO_VENDITE JOIN SCONTRINO
ON
DETTAGLIO_VENDITE.IDSCONTRINO =
SCONTRINO.IDSCONTRINO
GROUP BY ANNO, LIBRO
FACT_TABLE con push-­‐down: CREATE VIEW FACT_TABLE_PD AS
SELECT
ANNO,
P.LIBRO,
AUTORE,
INCASSO*PESO AS INCASSO_P,
NUMERO*PESO AS NUMERO_P,
NUMERO,
NUMEROCLIENTI*PESO AS
NUMEROCLIENTI_P,
NUMEROCLIENTI
FROM FACT_TABLE F JOIN
Peso P ON F.Libro =
P.LIBRO
12 SQL-­‐OLAP Per il pattern {AUTORE LIBRO } e sub-­‐pattern, la query SQL-­‐OLAP 1) per la presenza di un arco multiplo ed avendo usato la soluzione con push-­‐down: si deve effettuare il controllo e quindi il calcolo per le misure di impatto 2) per la presenza di una non aggregabilità di NUMCLIENTI rispetto a LIBRO, deve effettuare il relativo controllo Quello che si vuole ottenere è il seguente risultato Nel seguito per denotare il caso non aggregabile useremo per semplicità il segno – invece di (NA), ovvero Per la misura di impatto NUMERO : NUMERO = CASE
WHEN GROUPING(AUTORE)=1
THEN SUM(NUMERO_P)
ELSE SUM(NUMERO)
END
Il caso particolare è la misura di impatto NUMCLIENTI che risulta anche non aggregabile rispetto alla dimensione LIBRO, ovvero si devono effettuare due controlli 1) Per i pattern senza attributi della gerarchia dell’arco multiplo WHEN <PATTERN SENZA ATTRIBUTI GERARCHIA AM>
THEN <VALORE PESATO>
ELSE <VALORE NON PESATO> END
13 2) Nel calcolo si di <VALORE PESATO> che di <VALORE NON PESATO> si deve aggiungere l’indicazione di non aggregabilità rispetto a LIBRO tramite la condizione GROUPING(LIBRO)=1. Complessivamente NUMEROCLIENTI = CASE
WHEN GROUPING(AUTORE)=1
THEN
CASE WHEN GROUPING(LIBRO)=1
THEN
- SUM(NUMEROCLIENTI_P)
ELSE SUM(NUMEROCLIENTI_P)
END
ELSE
CASE
WHEN GROUPING(LIBRO)=1
THEN
- SUM(NUMEROCLIENTI)
ELSE SUM(NUMEROCLIENTI)
END
END
14 ESEMPIO DI PROVA PRATICA Sono dati Schema di Fatto VENDITE CITTA
LIBRO
INCASSO: misura pesata NUMERO: misura di impatto AUTORE
VENDITE
ANNO
NUMERO
INCASSO
NUMEROCLIENTI
GENERE
NUMEROCLIENTI: misura di impatto . DM con push-­‐down Backup del DM: http://www.dbgroup.unimo.it/SIA/DM_ArcoMultiploLIbro.bak 15 Viene richiesto di: 1) Realizzare il cubo con tutte le dimensioni e misure corrispondenti allo schema di Fatto VENDITE (2) Visualizzare in MDX il pattern {AUTORE,LIBRO } e sub-­‐pattern con tutte le misure di VENDITE (3.A) Scrivere in MDX la query per riportare il pattern {CITTA,GENERE} sulle colonne e tutte le misure di VENDITE sulle righe; (3.B) Scrivere quindi una query MDX per riportare sulle colonne il pattern {CITTA,GENERE} limitatamente alle coppie che hanno un INCASSO < 120 e un NUMERO < 3, visualizzandone l’INCASSO (4.A) Scrivere quindi in MDX la query per calcolare l’incasso minimo fatto registrare per una terna del pattern {AUTORE,LIBRO,ANNO} (4.B) Scrivere quindi in MDX la query per riportare il pattern {AUTORE,LIBRO,ANNO} sulle colonne limitatamente alle terne che hanno fatto registrare l’incasso minimo calcolato al punto precedente, visual;izzandone appunto tale incasso 16 SOLUZIONE Realizzare il cubo con tutte le dimensioni e misure corrispondenti allo schema di Fatto VENDITE Questo cubo, comprensivo delle misure calcolate discusse di seguito è disponibile all’indirizzo: http://www.dbgroup.unimo.it/SIA/DM_ArcoMultiploLibro.CAB Le indicazioni per effettuare il restore del DB OLAP sono al seguente indirizzo http://www.dbgroup.unimo.it/SIA/BacKupRestoreOLAP.pdf La misura pesata INCASSO viene definita sulla base del suo valore pesato (INCASSO_P) presente in FACT_TABLE_PD. Per le misure di impatto, NUMERO e NUMEROCLIENTI si procede definendo opportune misure calcolate; inoltre, la particolarità di questo case è dovuta al fatto che la misura di impatto NUMEROCLIENTI risulta anche non aggregabile rispetto alla dimensione LIBRO. Nella soluzione con push down, di una misura di impatto M, la FACT_TABLE_PD contiene il suo valore pesato (M_P) ed il suo valore non pesato (M_I). In SQL-­‐OLAP la misura di impatto veniva calcolata attraverso il seguente schema IF <PATTERN SENZA ATTRIBUTI GERARCHIA AM>
THEN <VALORE PESATO>
ELSE <VALORE NON PESATO>
Anche nel cubo la misura di impatto M viene calcolata con tale schema, definendo una nuova misura (un membro calcolato) come segue 1) Con M_I si definisce nel cubo una misura (non visibile) à M_I 2) Con M_P si definisce nel una misura (non visibile) à M_P 3) M è un membro calcolato tramite la seguente espressione Iif([Dimensione_PD].CurrentMember.level.name=”(All)”, [Measures].[M_P], [Measures].[M_I]) dove [Dimensione_PD] è la dimensione di cui è stato fatto il push down nella fact table. In altre parole, la condizione <PATTERN SENZA ATTRIBUTI GERARCHIA AM> viene espressa tramite [Dimensione_PD].CurrentMember.level.name=”(All)” 17 Nell’esempio in questione, per la misura di impatto NUMERO, la FACT_TABLE_PD contiene il suo valore pesato (NUMERO_P) ed il suo valore di impatto (chiamato in questo esempio ancora NUMERO) 1) Con NUMERO si definisce una misura (non visibile) à NUMERO_I 2) Con NUMERO_P si definisce una misura (non visibile) à NUMERO_P 3) Si definisce NUMERO come membro calcolato tramite la seguente espressione Iif([AUTORE].CurrentMember.level.name=”(All)”, [Measures].[NUMERO_P], [Measures].[NUMERO_I]) Consideriamo ora la misura di impatto NUMEROCLIENTI che risulta non aggregabile rispetto a libro. Ricordiamo che una misura M non aggregabile rispetto alla dimensione D, viene calcolata attraverso il seguente schema IF <PATTERN CONTIENE D>
THEN M
ELSE M + (NA)
Utilizzando il segno – per indicare il caso non aggregabile IF <PATTERN CONTIENE D>
THEN M
ELSE - M
Anche nel cubo, una misura M non aggegabile rispetto a D viene calcolata con tale schema, definendo una nuova misura (un membro calcolato) come segue 1) Con M si definisce nel cubo una misura à M1 2) M è un membro calcolato tramite la seguente espressione Iif([Dimensione_D].CurrentMember.level.name=[Dimensione_D].Livello_D.name, [Measures].[M1], -­‐ [Measures].[M1]) Questo schema vale anche nel caso in cui M è una misura calcolata, quindi per definire la misura di impatto NUMEROCLIENTI non aggregabile rispetto a libro: A) Si definisce una misura calcolata per NUMEROCLIENTI come misura di impatto B) Si utilizza la misura definita al punto A per definire NUMEROCLIENTI come non aggregabile rispetto a LIBRO; se vogliamo riservare il nome NUMEROCLIENTI a tale misura calcolata, dobbiamo chiamare diversamente la misura calcolata al punto A (useremo NUMEROCLIENTI_A) NUMEROCLIENTI_A Nell’esempio in questione, per la misura di impatto NUMEROCLIENTI, la FACT_TABLE_PD contiene il suo valore pesato (NUMEROCLIENTI_P) ed il suo valore di impatto (chiamato in questo esempio ancora NUMEROCLIENTI) 18 1) Con NUMEROCLIENTI si definisce una misura (non visibile) à NUMEROCLIENTI_I 2) Con NUMEROCLIENTI_P si definisce una misura (non visibile) à NUMEROCLIENTI_P 3) Si definisce NUMEROCLIENTI_A come membro calcolato tramite la seguente espressione IIF ([AUTORE].CurrentMember.level.name=”(All)”, [Measures].[ NUMEROCLIENTI_P], [Measures].[NUMEROCLIENTI_I]) Quindi si definisce NUMEROCLIENTI come IIF([LIBRO].CURRENTMEMBER.LEVEL.NAME = [LIBRO].[Libro].NAME, [Measures].[NUMEROCLIENTI_A], -­‐[Measures].[NUMEROCLIENTI_A]) Visualizzare in MDX il pattern {AUTORE,LIBRO } e sub-­‐pattern con tutte le misure di VENDITE SELECT { [Measures].[INCASSO], [Measures].[NUMERO], [Measures].[NUMEROCLIENTI] } ONCOLUMNS, NONEMPTYCROSSJOIN(UNION([AUTORE].[Autore].MEMBERS, [AUTORE].[(All)].MEMBERS), UNION([LIBRO].[Libro].MEMBERS, [LIBRO].[(All)].MEMBERS)) FROM VENDITE ON ROWS 19 Nota: La misura calcolata NUMEROCLIENTI si può definire anche direttamente, senza introdurre NUMEROCLIENTI_A e riportando l’espressione NUMEROCLIENTI_A direttamente nell’espressione di NUMEROCLIENTI IIF([LIBRO].CURRENTMEMBER.LEVEL.NAME = [LIBRO].[Libro].NAME, IIF([AUTORE].CurrentMember.level.name=”(All)”, [Measures].[ NUMEROCLIENTI_P], [Measures].[NUMEROCLIENTI_I]), -­‐IIF([AUTORE].CurrentMember.level.name=”(All)”, [Measures].[ NUMEROCLIENTI_P], [Measures].[NUMEROCLIENTI_I])) Per fare questa verifica, definiamo questa nuova misura calcolata nella precedente query MDX (cioè non usiamo più la misura calcolata NUMEROCLIENTI definita nel cubo ma definiamo una misura calcolata NC direttamente nella query MDX) WITH MEMBER [MEASURES].NC AS 'IIF([LIBRO].CURRENTMEMBER.LEVEL.NAME = [LIBRO].[LIBRO].NAME, IIF([AUTORE].CURRENTMEMBER.LEVEL.NAME="(ALL)", [MEASURES].[NUMEROCLIENTI_P], [MEASURES].[NUMEROCLIENTI_I]), -­‐IIF([AUTORE].CURRENTMEMBER.LEVEL.NAME="(ALL)", [MEASURES].[NUMEROCLIENTI_P], [MEASURES].[NUMEROCLIENTI_I]))' SELECT { [INCASSO], [NUMERO], [NC] } ON COLUMNS, NONEMPTYCROSSJOIN( UNION([AUTORE].[AUTORE].MEMBERS, [AUTORE].[(ALL)].MEMBERS), UNION([LIBRO].[LIBRO].MEMBERS, [LIBRO].[(ALL)].MEMBERS)) ON ROWS FROM VENDITE Si ottiene lo stesso risultato 20 QUERY MDX Le interrogazioni MDX riportate di seguito sono riferite al cubo disponibile all’indirizzo: http://www.dbgroup.unimo.it/SIA/DM_ArcoMultiploLibro.CAB (3.A) Pattern {CITTA,GENERE} sulle colonne e tutte le misure di VENDITE sulle righe: SELECT NONEMPTYCROSSJOIN( [AUTORE].[CITTA].MEMBERS , [LIBRO].[GENERE].MEMBERS ) ON COLUMNS, { [INCASSO], [NUMERO], [NUMEROCLIENTI] } ON ROWS FROM VENDITE (3.B) Scrivere quindi una query MDX per riportare sulle colonne il pattern {CITTA,GENERE} limitatamente alle coppie che hanno un INCASSO < 120 e un NUMERO < 3, visualizzandone l’INCASSO SELECT FILTER(NONEMPTYCROSSJOIN( [AUTORE].[CITTA].MEMBERS , [LIBRO].[GENERE].MEMBERS ), ([INCASSO] < 120 AND [NUMERO] < 3 ) ) ON COLUMNS FROM VENDITE WHERE [INCASSO] (4.A) Scrivere quindi in MDX la query per calcolare l’incasso minimo fatto registrare per una terna del pattern {AUTORE,LIBRO,ANNO} (4.B) Scrivere quindi in MDX la query per riportare il pattern {AUTORE,LIBRO,ANNO} sulle colonne limitatamente alle terne che hanno fatto registrare l’incasso minimo calcolato al punto precedente, visual;izzandone appunto tale incasso Conviene innanzitutto visualizzare il seguente pattern per verificare quanto richiesto dalle query La query (4.A) è semplice : 21 WITH MEMBER [Measures].INCASSOMINIMO AS 'MIN( NONEMPTYCROSSJOIN( [AUTORE].[AUTORE].MEMBERS , [LIBRO].[LIBRO].MEMBERS, [Anno].[Anno].MEMBERS ), INCASSO)' SELECT { INCASSOMINIMO } ON COLUMNS FROM VENDITE La query (4.B) si ottiene filtrando il cross join al caso semplice INCASSO = INCASSOMINIMO WITH MEMBER [Measures].INCASSOMINIMO AS 'MIN( NONEMPTYCROSSJOIN( [AUTORE].[AUTORE].MEMBERS , [LIBRO].[LIBRO].MEMBERS, [Anno].[Anno].MEMBERS ), INCASSO)' SELECT FILTER( NONEMPTYCROSSJOIN( [AUTORE].[AUTORE].MEMBERS, [LIBRO].[LIBRO].MEMBERS, [Anno].[Anno].MEMBERS), INCASSO = INCASSOMINIMO) ON COLUMNS FROM VENDITE WHERE INCASSOMINIMO Con il semplice BOTTOMCOUNT 1 si ottiene un’unica terna: SELECT BOTTOMCOUNT( NONEMPTYCROSSJOIN( [AUTORE].[AUTORE].MEMBERS, [LIBRO].[LIBRO].MEMBERS, [Anno].[Anno].MEMBERS ), 1, INCASSO) ON COLUMNS FROM VENDITE 22 18 MAGGIO SELECT FILTER (
NONEMPTYCROSSJOIN(
[Autore].[Citta].MEMBERS,
[Libro].[Genere].MEMBERS
),
NUMERO > 1 ) ON COLUMNS,
[Anno].MEMBERS
ON ROWS FROM
VENDITE WHERE NUMERO WITH MEMBER MEASURES.INCASSOMINIMOPERANNO
AS ' MIN(ANNO.ANNO.MEMBERS, INCASSO)'
SELECT LIBRO.GENERE.MEMBERS
ON
COLUMNS FROM VENDITE
WITH MEMBER
MEASURES.INCASSOMINIMOPERANNOATTUALITA AS
MIN(ANNO.ANNO.MEMBERS,
(INCASSO,LIBRO.ATTUALITA) )'
SELECT LIBRO.GENERE.MEMBERS ON COLUMNS,
{ INCASSOMINIMOPERANNOATTUALITA,INCASSO } ON ROWS
FROM VENDITE
WITH MEMBER
MEASURES.INCASSOMINIMOPERANNOATTUALITA
AS ' MIN(ANNO.ANNO.MEMBERS,
(INCASSO,LIBRO.ATTUALITA) )'
SELECT LIBRO.GENERE.MEMBERS
ON COLUMNS,
CROSSJOIN(ANNO.ANNO.MEMBERS,
{INCASSOMINIMOPERANNOATTUALITA,
INCASSO } ) ON ROWS FROM VENDITE
WITH SET SETA AS 'NONEMPTYCROSSJOIN(
AUTORE.AUTORE.MEMBERS,
LIBRO.LIBRO.MEMBERS,
ANNO.ANNO.MEMBERS)' MEMBER MEASURES.MINIMO
AS 'MIN(SETA,INCASSO)' SELECT
{MEASURES.MINIMO} ON COLUMNS FROM VENDITE
WITH SET SETA AS 'NONEMPTYCROSSJOIN(
AUTORE.AUTORE.MEMBERS,
LIBRO.LIBRO.MEMBERS,
ANNO.ANNO.MEMBERS)'
MEMBER MEASURES.MINIMO AS 'MIN(SETA,INCASSO)'
SELECT FILTER(SETA,INCASSO=MINIMO) ON COLUMNS FROM VENDITE
WITH SET SETA AS 'NONEMPTYCROSSJOIN(
AUTORE.AUTORE.MEMBERS,
LIBRO.LIBRO.MEMBERS,
ANNO.ANNO.MEMBERS)'
MEMBER MEASURES.MINIMO AsMIN(SETA,INCASSO)'
SELECT BOTTOM(SETA,3,INCASSO) ON COLUMNS FROM VENDITE
23 NUOVE QUERY: VENGONO INSERITI ALTRI DUE LIBRI CON RELATIVE VENDITE: WITH
SET AUTORIMODENA AS 'AUTORE.MO.CHILDREN'
SET LIBRIATTUALITA AS 'LIBRO.Attualita.CHILDREN'
SET LIBRIATTUALITA_AUTORIMODENA AS
'NONEMPTYCROSSJOIN(LIBRIATTUALITA, AUTORIMODENA)'
SET BESTSELLERATTUALITA AS 'FILTER(LIBRIATTUALITA,INCASSO>100)
SET BESTSELLERATTUALITAMODENA AS 'FILTER(LIBRIATTUALITA_AUTORIMODENA,INCASSO>100)
SELECT
AUTORIMODENA ON COLUMNS,
LIBRIATTUALITA ON ROWS
FROM VENDITE
SELECT
AUTORIMODENA ON COLUMNS,
NON EMPTY LIBRIATTUALITA ON ROWS
FROM VENDITE
SELECT BESTSELLERATTUALITA ON COLUMNS
FROM VENDITE
SELECT
AUTORIMODENA ON COLUMNS,
BESTSELLERATTUALITA ON ROWS
FROM VENDITE
SELECT BESTSELLERATTUALITAMODENA ON COLUMNS
FROM VENDITE
24 BESTSELLER RIFERITI AGLI AUTORI Per gli autori di Modena (columns) riportare sulle rows Libri Attualità con il relativo incasso Numero e Incasso dei bestseller (inteso come libro con INCASSO > 100) L’asse COLUMNS è costituito da AUTORIMODENA L’asse ROWS è costituito dall’unione di LIBRIATTUALITA con altri tre membri calcolati, che devono essere definiti come membri calcolati della dimensione LIBRI (ovvero della stessa dimensione LIBRIATTUALITA) allo scopo di ottenere elementi omogenei e poter fare l’unione. Ad esempio per aggiungere la linea trattegiata WITH
SET AUTORIMODENA AS 'AUTORE.MO.CHILDREN'
SET LIBRIATTUALITA AS 'LIBRO.Attualita.CHILDREN'
MEMBER [LIBRO].[linea] AS ' "----------" '!!
SELECT
AUTORIMODENA ON COLUMNS,!
UNION(LIBRIATTUALITA,{ [LIBRO].[linea]}) !
FROM VENDITE
ON ROWS!
Anche NUMEROBESTSELLER deve essere un Membro Calcolato della Dimensione LIBRO che deve essere unito sull’asse ROWS WITH
SET AUTORIMODENA AS 'AUTORE.MO.CHILDREN'
SET LIBRIATTUALITA AS 'LIBRO.Attualita.CHILDREN'
SET LIBRIATTUALITA_AUTORIMODENA AS
'NONEMPTYCROSSJOIN(LIBRIATTUALITA, AUTORIMODENA)'
SET BESTSELLERATTUALITA AS 'FILTER(LIBRIATTUALITA,INCASSO>100)
SET BESTSELLERATTUALITAMODENA AS 'FILTER(LIBRIATTUALITA_AUTORIMODENA,INCASSO>100)
MEMBER [LIBRO].[linea] AS ' "----------" '!!
MEMBER LIBRO.NUMEROBESTSELLER AS 'COUNT(BESTSELLERATTUALITAMODENA)'
SELECT
AUTORIMODENA ON COLUMNS,!
UNION(LIBRIATTUALITA,{ [LIBRO].[linea], LIBRO.NUMEROBESTSELLER }) ! ON ROWS!
FROM VENDITE
Però se contiamo gli elementi di BESTSELLERATTUALITAMODENA si ottiene sempre il valore 3, in quanto si contano gli elementi di BESTSELLERATTUALITAMODENA indipendentemente dalla colonna (cioè dall’autore): con SET si definisce un insieme e tale insieme non viene contestualizzato nella query corrente (vedere pagina che segue per un esempio più semplice). Per ottenere BESTSELLER RIFERITI AGLI AUTORI occorre contare rispetto agli autori, cioè fare un conteggio per ciascuno degli autori è serve AUTORE.CURRENTMEMBER 25 Consideriamolo prima in un esempio più semplice QUERY A
WITH
SET AUTORIMODENA AS 'AUTORE.MO.CHILDREN'
SET LIBRIATTUALITA AS 'LIBRO.Attualita.CHILDREN'
MEMBER [LIBRO].CONTA AS
'COUNT(NONEMPTYCROSSJOIN
(LIBRIATTUALITA,!
{AUTORE.CURRENTMEMBER } ))'!
SELECT
AUTORIMODENA ON COLUMNS,!
UNION(LIBRIATTUALITA,{[LIBRO].CONTA }) ON ROWS
FROM VENDITE
Con AUTORE.CURRENTMEMBER il conteggio viene riferito all’AUTORE corrente definito nel contesto della query, e quindi AutoreA e AutoreB. Se la query non definisce tali autori, cioè non usa la dimensione AUTORE WITH
SET AUTORIMODENA AS 'AUTORE.MO.CHILDREN'
SET LIBRIATTUALITA AS
'LIBRO.Attualita.CHILDREN'
MEMBER [LIBRO].CONTA AS
'COUNT(NONEMPTYCROSSJOIN
(LIBRIATTUALITA,!
{AUTORE.CURRENTMEMBER} ))'!
SELECT
UNION(LIBRIATTUALITA,{[LIBRO].CONTA })
ON COLUMNS
FROM VENDITE
AUTORE.CURRENTMEMBER è equivalente a AUTORE.[All Autore] Nella QUERY A non si può definire l’insieme del conteggio tramite un SET WITH SET SETA AS
'NONEMPTYCROSSJOIN (
LIBRIATTUALITA,!
{AUTORE.CURRENTMEMBER} )'
!MEMBER [LIBRO].CONTA AS 'COUNT(SETA)'!
SELECT
AUTORIMODENA ON COLUMNS,!
UNION(LIBRIATTUALITA,{[LIBRO].CONTA }) ! ON ROWS
FROM VENDITE In quanto con SET si definisce un insieme e tale insieme non viene contestualizzato nella query corrente e pertanto si ottiene sempre 4, per entrambi gli autori WITH SET SETA AS
'NONEMPTYCROSSJOIN (
LIBRIATTUALITA,!
{AUTORE.CURRENTMEMBER} )'
SELECT
SETA ON COLUMNS
FROM VENDITE 26 Per il membro INCASSOBESTSELLER il ragionamento è analogo; quindi in definitiva : WITH
SET AUTORIMODENA AS 'AUTORE.MO.CHILDREN'
SET LIBRIATTUALITA AS 'LIBRO.Attualita.CHILDREN'
MEMBER [LIBRO].NUMEROBESTSELLER AS!
'COUNT(FILTER(NONEMPTYCROSSJOIN (
LIBRIATTUALITA,!
{AUTORE.CURRENTMEMBER }),
INCASSO>100))'!
!MEMBER [LIBRO].INCASSOBESTSELLER AS!
'SUM(FILTER(NONEMPTYCROSSJOIN (
LIBRIATTUALITA,!
{AUTORE.CURRENTMEMBER } ), !
INCASSO>100),
INCASSO)'!
!MEMBER [LIBRO].[-------------] AS ' "----------" '!!
SELECT
AUTORIMODENA ON COLUMNS,!
UNION(LIBRIATTUALITA,{[LIBRO].[-------------] ,
[LIBRO].NUMEROBESTSELLER , [LIBRO].INCASSOBESTSELLER
FROM VENDITE
}) !
ON ROWS!
27 Consideriamo il Libro MIGLIORE definito come: WITH
SET AUTORIMODENA AS 'AUTORE.MO.CHILDREN'
SET LIBRIATTUALITA AS 'LIBRO.Attualita.CHILDREN'
MEMBER [LIBRO].MIGLIORE AS
'MAX(NONEMPTYCROSSJOIN
(LIBRIATTUALITA,!
{AUTORE.CURRENTMEMBER } ),INCASSO)'!
SELECT
AUTORIMODENA ON COLUMNS,!
NON EMPTY UNION(LIBRIATTUALITA,
{[LIBRO].MIGLIORE}) ON ROWS
FROM VENDITE
COME EVIDENZIARE I MIGLIORI? Cioè ottenere l’indicazione (BEST) accanto ai valori visualizzati
Concettualmente la soluzione è semplice: si definisce una nuova misura MEMBER [MEASURES].NEWINCASSO AS 'IIF(INCASSO=([LIBRO].MIGLIORE,INCASSO), INCASSO + "(BEST)", INCASSO)' che viene visualizzata tramite WHERE [MEASURES].NEWINCASSO (l’operatore + rappresenta la concatenazione tra stringhe). Però prima della concatenazione si deve convertire il valore INCASSO (un valore numerico) in una stringa cstr WITH
SET AUTORIMODENA AS 'AUTORE.MO.CHILDREN'
SET LIBRIATTUALITA AS 'LIBRO.Attualita.CHILDREN'
MEMBER [LIBRO].MIGLIORE AS
'MAX(NONEMPTYCROSSJOIN
(LIBRIATTUALITA,!
{AUTORE.CURRENTMEMBER } ),INCASSO)'!
MEMBER [MEASURES].NEWINCASSO AS
'IIF(INCASSO=([LIBRO].MIGLIORE,INCASSO),
cstr(INCASSO) + "(BEST)", cstr(INCASSO))'!
SELECT
AUTORIMODENA ON COLUMNS,!
NON EMPTY UNION(LIBRIATTUALITA,{[LIBRO].MIGLIORE}) ON ROWS
FROM VENDITE
WHERE [MEASURES].NEWINCASSO
Si noti che anche nel lato false dell’IIF ci vuole cstr(INCASSO) in quanto la funzione IIF deve avere la parte true e la parte false dello stesso tipo … E’ facile verificare che l’errore è dovuto alla conversione delle celle vuote: queste celle vuote sono nella parte false dell’IIF, quindi invece del semplice cstr(INCASSO) si riporta un IIF per controllare appunto le celle vuote !
IIF(isempty(INCASSO), "
" , cstr(INCASSO))
28 WITH
SET AUTORIMODENA AS 'AUTORE.MO.CHILDREN'
SET LIBRIATTUALITA AS 'LIBRO.Attualita.CHILDREN'
MEMBER [LIBRO].MIGLIORE AS
'MAX(NONEMPTYCROSSJOIN
(LIBRIATTUALITA,!
{AUTORE.CURRENTMEMBER } ),INCASSO)'!
MEMBER [MEASURES].NEWINCASSO AS
'IIF(INCASSO=([LIBRO].MIGLIORE,INCASSO),
cstr(INCASSO) + "(BEST)",
IIF(isempty(INCASSO), " " , cstr(INCASSO)))'!
SELECT
AUTORIMODENA ON COLUMNS,!
NON EMPTY UNION(LIBRIATTUALITA,{[LIBRO].MIGLIORE}) ON ROWS
FROM VENDITE
WHERE [MEASURES].NEWINCASSO
L’ultimo dettaglio è la visualizzazione del valore corrispondente al membro MIGLIORE, dal quale si vuole togliere (BEST); occorre differenziare cosa si visualizza tra il membro MIGLIORE e gli altri membri di LIBRO: allora si definisce una nuova misura MEMBER [MEASURES].NEWINCASSO2 AS !
'IIF(LIBRO.CURRENTMEMBER.NAME ="MIGLIORE", INCASSO, NEWINCASSO) '
WITH
SET AUTORIMODENA AS 'AUTORE.MO.CHILDREN'
SET LIBRIATTUALITA AS 'LIBRO.Attualita.CHILDREN'
MEMBER [LIBRO].MIGLIORE AS
'MAX(NONEMPTYCROSSJOIN
(LIBRIATTUALITA,!
{AUTORE.CURRENTMEMBER } ),INCASSO)'!
MEMBER [MEASURES].NEWINCASSO AS
'IIF(INCASSO=([LIBRO].MIGLIORE,INCASSO),
cstr(INCASSO) + "(BEST)",
IIF(isempty(INCASSO), " " , cstr(INCASSO)))'!
MEMBER [MEASURES].NEWINCASSO2 AS !
'IIF(LIBRO.CURRENTMEMBER.NAME ="MIGLIORE",
INCASSO, NEWINCASSO) '
SELECT
AUTORIMODENA ON COLUMNS,!
NON EMPTY UNION(LIBRIATTUALITA,{[LIBRO].MIGLIORE}) ON ROWS
FROM VENDITE
WHERE [MEASURES].NEWINCASSO2
29