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