Podstawowe zapytanie MDX ma strukturę podobną do zapytań SQL. Najprostsza postać zwraca dwuwymiarową kostkę: opis osi ON COLUMNS, opis osi ON ROWS FROM nazwa_kostki [WHERE opis_plastra] Najprostsza postać opisu osi lub wyboru członów używa MEMBERS jako wymaganego wymiaru, łącznie ze wymiarem Measures: Measures.MEMBERS ON COLUMNS, [Nazwa kategorii].members ON ROWS Opis osi może być traktowany jako wybór członu dla osi. Jeżeli wymagany jest pojedynczy wymiar, przy użyciu takiego zapisu musi być zwrócone COLUMNS. Dla większej ilości wymaganych wymiarów nazwami osi byłyby PAGES (strony), CHAPTERS (rozdziały) oraz SECTIONS (sekcje). Jeśli chcemy użyć bardziej uniwersalnych określeń osi, możemy użyć konwencji: AXIS(index), gdzie index jest zakresem rozpoczynającym się od zera. W Management Studio dopuszczalne tylko dwie osie. Jeżeli chcemy wyliczyć elementy wymiaru, mogą one być zwrócone jako pojedyncza oś (lista ograniczona {...} rozdzielona, : Measures.MEMBERS ON COLUMNS, {[Nazwa kategorii].[clocks], [Nazwa kategorii].[sampler] } ON ROWS Struktura kostki [BiznesG] 1
Parents Descendants ANCESTOR(Time.[2000].[Q1], Time.[Year]) Time.[2000].[Q1].Parent.Parent Time.[2000].Parent All Time.[2001].Parent All Time.[2000].[Q1].Parent 2000 2001 Descendants( Time.[2000], Quarter) 2000 2001 Q1 Q2 Q3 Q4 Q1 Q2 Q3 Q4 Q1 Q2 Q3 Q4 Q1 Q2 Q3 Q4 Jan Feb Mar Oct Nov Dec Jan Feb Mar Oct Nov Dec Descendants( Time.[2000], Month) Children Descendants All All Time.[2000].FirstChild Time.[2000].Children 2000 2001 2000 2001 Q1 Q2 Q3 Q4 Q1 Q2 Q3 Q4 Q1 Q2 Q3 Q4 Q1 Q2 Q3 Q4 Jan Feb Mar Oct Nov Dec Jan Feb Mar Oct Nov Dec Descendants(Time.[2000].[Jan], Month) 2
Aby uzyskać miary dla członów składających się na te kategorie, zapytalibyśmy ich dzieci (CHILDREN): Measures.MEMBERS ON COLUMNS, {[KategoriaProduktuH].[Nazwa kategorii].[clocks].children, [KategoriaProduktuH].[Nazwa kategorii].[sampler].children } ON ROWS Miary obliczane nie są dołączane, gdy pobierane są człony wymiarów. Ich uwzględnienie musi być jawnie narzucone, przez użycie funkcji ADDCALCULATEDMEMBERS:. ADDCALCULATEDMEMBERS(Measures.MEMBERS) ON COLUMNS, {[KategoriaProduktuH].[Nazwa kategorii].[clocks], DESCENDANTS( [KategoriaProduktuH].[Nazwa kategorii].[clocks],[nazwa Towaru])}ON ROWS Funkcja MEMBERS zwraca elementy wskazanego wymiaru lub poziomu wymiarów. Funkcja CHILDREN zwraca dzieci dla określonego członu wymiaru. Obie funkcje są używane przy formułowaniu wyrażeń, ale nie zapewniają możliwości rozwijania do niższych poziomów hierarchii. Przy użyciu funkcji DESCENDANTS możliwe się staje zapytanie kostki o informacje na poziomie nazwa towaru dla wybranej kategorii Measures.MEMBERS ON COLUMNS, { [KategoriaProduktuH].[Nazwa kategorii].[clocks], DESCENDANTS( [KategoriaProduktuH].[Nazwa kategorii].[clocks], [Nazwa Towaru]) }ON ROWS Wybór miar do wyświetlenia {MEASURES.wartosc, MEASURES.zysk} ON COLUMNS, { [KategoriaProduktuH].[Nazwa kategorii].[clocks], DESCENDANTS( [KategoriaProduktuH].[Nazwa kategorii].[clocks], [Nazwa Towaru]) }ON ROWS DESCENDANTS(człon, poziom [, flagi]) Wartość flagi może wynosić: SELF (domyślna wartość), BEFORE, AFTER lub BEFORE_AND_AFTER 3
Zastosowanie krotki na wymiarach w celu wyświetlenia hierarchii ADDCALCULATEDMEMBERS(Measures.MEMBERS) ON COLUMNS, {CROSSJOIN( [KategoriaTowaruG].[Nazwa kategorii].members, [KategoriaProduktuH].[Nazwa Towaru].members)}ON ROWS Każda pozycja z jednego wymiaru z każdą z drugiego CROSSJOIN nie może być zastosowany do dwa razy występującego tego samego wymiaru Zastosowanie krotki na wymiarach w celu wyświetlenia hierarchii ADDCALCULATEDMEMBERS(Measures.MEMBERS) ON COLUMNS, { NONEMPTY (CROSSJOIN( FILTER ( [KategoriaTowaruG].[Nazwa kategorii].members, [KategoriaTowaruG].[Nazwa kategorii]<>[kategoriatowarug].[nazwa kategorii].[all] ), [KategoriaProduktuH].[Nazwa Towaru].members)) }ON ROWS Wyeliminowanie elementu ALL reprezentującego wszystkie obiekty poziomu nadrzędnego osiągamy przez zastosowanie funkcji FILTER Zastosowanie krotki na wymiarach w celu wyświetlenia hierarchii ADDCALCULATEDMEMBERS(Measures.MEMBERS) ON COLUMNS, { NONEMPTY (CROSSJOIN( [KategoriaTowaruG].[Nazwa kategorii].members, [KategoriaProduktuH].[Nazwa Towaru].members)) }ON ROWS Nie zastosowanie krotki powoduje spłaszczenie wyświetlania Measures.MEMBERS ON COLUMNS, { [KategoriaProduktuH].[Nazwa kategorii].members, [KategoriaProduktuH].[Nazwa Towaru].members}ON ROWS W celu wyeliminowania niepowiązanych rekordów zastosować możemy operator NONEMPTY Nazwy towarów dla kategorii CLOCKS 4
Specyfikację plastra definiuje się przez klauzulę WHERE [Nazwa Kategorii].MEMBERS ON COLUMNS, [LokalizacjaKlientaH].[Województwo].MEMBERS ON ROWS WHERE ([Measures].[wartosc]) [KategoriaTowaruG].[Nazwa Kategorii].MEMBERS ON COLUMNS, [LokalizacjaKlientaH].[Województwo].MEMBERS ON ROWS WHERE ([Measures].[wartosc], [Towar G].[Nazwa Kategorii].[CLOCKS]) Definiowanie plastra z wykorzystaniem dwóch wymiarów zawierających ten sam poziom Mało eleganckie ze względu na wartości NULL Bardziej szczegółową definicję plastra można osiągnąć przez zdefiniowanie konkretnej wartości wymiaru [Nazwa Kategorii].MEMBERS ON COLUMNS, [LokalizacjaKlientaH].[Województwo].MEMBERS ON ROWS WHERE ([Measures].[wartosc], [Data Faktury].[Rok].[2003]) Nie można do definiowania plastra użyć wymiaru poprzednio wykorzystanego do definiowania kolumn lub wierszy NONEMPTY([KategoriaTowaruG].[Nazwa Kategorii].MEMBERS) ON COLUMNS, [LokalizacjaKlientaH].[Województwo].MEMBERS ON ROWS WHERE ([Measures].[wartosc], [Towar G].[Nazwa Kategorii].[CLOCKS]) Definiowanie plastra z wykorzystaniem dwóch wymiarów zawierających ten sam poziom Pominięcie wartości NULL przy użyciu NONEMPTY 5
[KategoriaTowaruG].[Nazwa Kategorii].[CLOCKS] ON COLUMNS, [LokalizacjaKlientaH].[Województwo].MEMBERS ON ROWS WHERE ([Measures].[wartosc]) Definiowanie plastra z wykorzystaniem jawnego wskazania na wartość w kolumnie Możliwe jest wyznaczenie członów wyliczanych Ad hoc przy zastosowaniu klauzuli oraz miary wyliczanej MEMBER Measures.ZyskProcentowy AS '([Measures].[zysk]) / [Measures].[WartoscZakupu]', FORMAT_STRING = '#.00%' [KategoriaTowaruG].[Nazwa Kategorii].MEMBERS ON COLUMNS, [LokalizacjaKlientaH].[Województwo].MEMBERS ON ROWS WHERE Measures.ZyskProcentowy Możliwe jest wyznaczenie członów wyliczanych Ad hoc przy zastosowaniu klauzuli MEMBER Measures.ZyskProcentowy AS '([Measures].[Wartosc] - [Measures].[WartoscZakupu]) / [Measures].[WartoscZakupu]', FORMAT_STRING = '#.00%' [KategoriaTowaruG].[Nazwa Kategorii].MEMBERS ON COLUMNS, [LokalizacjaKlientaH].[Województwo].MEMBERS ON ROWS WHERE Measures.ZyskProcentowy Możliwe jest wyznaczenie członów wyliczanych Ad hoc przy zastosowaniu klauzuli MEMBER Measures.ZyskProcentowy AS ([Measures].[Wartosc] - [Measures].[WartoscZakupu]) / [Measures].[WartoscZakupu], FORMAT_STRING = '0.000%' ZyskProcentowy ON COLUMNS, [LokalizacjaKlientaH].[Województwo].MEMBERS ON ROWS 6
Możliwe jest wyznaczenie członów wyliczanych Ad hoc przy zastosowaniu klauzuli i odczytanie ich przez ADDCALCULATEDMEMBERS MEMBER Measures.ZyskProcentowy AS '([Measures].[zysk]) / [Measures].[WartoscZakupu]', FORMAT_STRING = '#.00%' ADDCALCULATEDMEMBERS(MEASURES.MEMBERS) ON COLUMNS, [LokalizacjaKlientaH].[Województwo].MEMBERS ON ROWS MEMBER Measures.ZyskProcentowy AS '([Measures].[zysk]) /[Measures].[WartoscZakupu]', FORMAT_STRING = '#.##%' MEMBER [Data Faktury].[Miesiac].PierwszaPolowa AS '[Miesiac].[1]+[Miesiac].[2]+[Miesiac].[3]+[Miesiac].[4]+[Miesiac].[5]+[Miesiac].[6]' MEMBER [Data Faktury].[Miesiac].DrugaPolowa AS '[Miesiac].[7]+[Miesiac].[8]+[Miesiac].[9]+[Miesiac].[10]+[Miesiac].[11] +[Miesiac].[12]' {PierwszaPolowa, DrugaPolowa, [Data Faktury].Miesiac.MEMBERS} ON COLUMNS, [KategoriaTowaruG].[Nazwa Kategorii].MEMBERS on ROWS WHERE Measures.ZyskProcentowy MEMBER Measures.ZyskProcentowy AS '([Measures].[zysk]) /[Measures].[WartoscZakupu]', FORMAT_STRING = '#.##%' MEMBER [Data Faktury].[Miesiac].PierwszaPolowa AS '[Miesiac].[1]+[Miesiac].[2]+[Miesiac].[3]+[Miesiac].[4]+[Miesiac].[5]+[Miesiac].[6]' MEMBER [Data Faktury].[Miesiac].DrugaPolowa AS '[Miesiac].[7]+[Miesiac].[8]+[Miesiac].[9]+[Miesiac].[10]+ [Miesiac].[11]+[Miesiac].[12]' {PierwszaPolowa, DrugaPolowa} ON columns, [KategoriaTowaruG].[Nazwa Kategorii].members on rows WHERE Measures.ZyskProcentowy Używając obliczanych członów możemy łatwo zdefiniować nowy wymiar CZASU służący do przedstawienia roku z rozbiciem na połowy : MEMBER Measures.ZyskProcentowy AS '([Measures].[zysk]) /[Measures].[WartoscZakupu]', FORMAT_STRING = '#.##%, SOLVE_ORDER=1 MEMBER [Data Faktury].[Miesiac].PierwszaPolowa AS '[Miesiac].[1]+[Miesiac].[2]+[Miesiac].[3]+[Miesiac].[4]+[Miesiac].[5]+[Miesiac].[6]' MEMBER [Data Faktury].[Miesiac].DrugaPolowa AS '[Miesiac].[7]+[Miesiac].[8]+[Miesiac].[9]+[Miesiac].[10]+[Miesiac].[11] +[Miesiac].[12]' {PierwszaPolowa, DrugaPolowa, [Data Faktury].Miesiac.MEMBERS} ON COLUMNS, [KategoriaTowaruG].[Nazwa Kategorii].MEMBERS on ROWS WHERE Measures.ZyskProcentowy Wymuszenie kolejność obliczania miar wyliczanych za pomocą SOLVE_ORDER=? 7
MEMBER Measures.ZyskProcentowy AS '([Measures].[zysk]) /[Measures].[WartoscZakupu]', FORMAT_STRING = '#.##%', SOLVE_ORDER=1 MEMBER [Data Faktury].[Kwartal].PierwszaPolowa AS '[Kwartal].[1]+[Kwartal].[2]' MEMBER [Data Faktury].[Kwartal].DrugaPolowa AS '[Kwartal].[3]+[Kwartal].[4]' {PierwszaPolowa, DrugaPolowa,[Data Faktury].Kwartal.MEMBERS} ON COLUMNS, [KategoriaTowaruG].[Nazwa Kategorii].MEMBERS ON ROWS WHERE Measures.ZyskProcentowy 1. How did Sales this period compare with Sales in the previous period? Q2 Qtr Apr May Jun Mon Sales 200 65 45 90 Delta (DELTA) = 80 15-20 45 (Time.CurrentMember, Measures.Sales) - Podobna realizacja ale dla wprowadzonej hierarchii Kwartal (Time.CurrentMember.PrevMember, Measures.Sales) Porównanie z poprzednim okresem Year Qtr Mon Sales 2000 790 Q1 120 Jan 30 Feb 40 Mar 50 Q2 200 Apr 65 May 45 Jun 90 Q3 185 Jul 55 Aug 60 Sep 70 Q4 285 Oct 80 Nov 100 Dec 105 Porównanie z równoległym okresem poprzedniego roku Year Qtr Mon Sales 2000 790 Q1 120 Jan 30 Feb 40 Mar 50 Q4 285 Oct 80 Nov 100 Dec 105 2001 850 Q1 170 Jan 50 Feb 55 Mar 65 Q4 275 Oct 90 Nov 100 Dec 85 8
Porównanie z równoległym okresem poprzedniego roku Year Qtr Mon Sales Delta 2000 790 Q1 120 Jan 30 Feb 40 Mar 50 2001 850 Q1 170 Jan 50 20 Feb 55 Mar 65 (DELTA)= (Time.CurrentMember, Measures.Sales)- Źle (Time.CurrentMember.Lag(12), (ParallelPeriod(Year,1,Time.CurrentMember), Measures.Sales) Measures.Sales) 3. What have Sales been since the beginning of the year? Year Qtr Mon Sales YTD 2000 790 Q1 120 120 Jan 30 30 Feb 40 70 Mar 50 120 Time.Jan,Measures.Sales + Sum(YTD(Time.CurrentMember), Time.Feb,Measures.Sales + Źle Sales) Time.Mar,Measures.Sales Wyznaczanie sumy bieżącej od początku roku Year Qtr Mon Sales 2000 790 Q1 120 Jan 30 Feb 40 Mar 50 Q2 200 Apr 65 May 45 Jun 90 Q3 185 Jul 55 Aug 60 Sep 70 Q4 285 Oct 80 Nov 100 Dec 105 3. What have Sales been since the beginning of the year? Year Qtr Mon Sales 2000 790 Q1 120 Jan 30 Feb 40 Mar 50 Time.Jan,Measures.Sales + Time.Feb,Measures.Sales + Time.Mar,Measures.Sales Sum(YTD(Time.CurrentMember), Sales) = YTD 120 30 70 120 9
możemy wyświetlić zysk dla każdej kategorii towaru w każdym pierwszym kwartale (pierwszy potomek hierarchii) każdego roku: SET [Kwartal1] AS GENERATE([Data Faktury].[DataFakturyH].[Rok].MEMBERS, {[Data Faktury].[DataFakturyH].CURRENTMEMBER.FIRSTCHILD}) [Kwartal1] ON COLUMNS, [Towar G].[Nazwa Kategorii].MEMBERS ON ROWS WHERE ([Measures].[Zysk]) możemy wyświetlić zysk dla każdej kategorii towaru w każdym pierwszym kwartale (pierwszy potomek hierarchii - numeracja od 0) każdego roku: SET [Kwartal1] AS GENERATE([Data Faktury].[DataFakturyH].[Rok].MEMBERS, {[Data Faktury].[DataFakturyH].CURRENTMEMBER.children(0)}) [Kwartal1] ON COLUMNS, [Towar G].[Nazwa Kategorii].MEMBERS ON ROWS WHERE ([Measures].[Zysk]) możemy wyświetlić zysk dla każdej kategorii towaru w każdym ostatnim kwartale (Ostatni potomek hierarchii o ile istnieje) każdego roku: SET [Kwartal1] AS GENERATE([Data Faktury].[DataFakturyH].[Rok].MEMBERS, {[Data Faktury].[DataFakturyH].CURRENTMEMBER.LASTCHILD}) [Kwartal1] ON COLUMNS, [Towar G].[Nazwa Kategorii].MEMBERS ON ROWS WHERE ([Measures].[Zysk]) możemy wyświetlić zysk dla każdej kategorii towaru w każdym pierwszym kwartale (czwarty potomek hierarchii - numeracja od 0) każdego roku: SET [Kwartal1] AS GENERATE([Data Faktury].[DataFakturyH].[Rok].MEMBERS, {[Data Faktury].[DataFakturyH].CURRENTMEMBER.children(3)}) [Kwartal1] ON COLUMNS, [Towar G].[Nazwa Kategorii].MEMBERS ON ROWS WHERE ([Measures].[Zysk]) Gdy niezdefiniowano dla któregoś roku nie jest wyświetlany 10
Wyznaczmy sprzedaż danej marki produktu jako udział procentowy w sprzedaży jego kategorii czyli atrybutu, w odniesieniu do jego rodzica. Wyrażenie tworzące taki miarę obliczaną może zostać uzyskane przy użyciu właściwości CURRENTMEMBER oraz PARENT. MEMBER MEASURES.ZyskKategorii AS ([KategoriaProduktuH].CURRENTMEMBER.PARENt, [Measures].[Zysk]) MEMBER MEASURES.ZyskProcentowy AS ([KategoriaProduktuH].CURRENTMEMBER, [Measures].[Zysk])/ ([KategoriaProduktuH].CURRENTMEMBER.PARENT, [Measures].[Zysk]), FORMAT_STRING = '0.00%' {Measures.ZyskKategorii, [Measures].[Zysk], MEASURES.ZyskProcentowy} ON COLUMNS, [KategoriaProduktuH].[Nazwa Towaru] ON ROWS Wielokrotne użycie funkcji PARENT może być zastąpione przez obliczenie odpowiednich przodków członu CURRENTMEMBER przez funkcje ANCESTOR, zwracającą przodka na odpowiednim poziomie dla podanego członu. Konieczne jest użycie nazwy hierarchii: MEMBER MEASURES.ZyskKategorii AS (ANCESTOR([KategoriaProduktuH].CURRENTMEMBER, [KategoriaProduktuH].[Nazwa Kategorii]), [Measures].[Zysk]) MEMBER MEASURES.ZyskProcentowy AS ([KategoriaProduktuH].currentmember, [Measures].[Zysk])/ (ANCESTOR([KategoriaProduktuH].CURRENTMEMBER, [KategoriaProduktuH].[Nazwa Kategorii]), [Measures].[Zysk]), FORMAT_STRING = '0.00%' {MEASURES.ZyskKategorii,[Measures].[Zysk], MEASURES.ZyskProcentowy} ON COLUMNS, [KategoriaProduktuH].[nazwa towaru] ON ROWS PARENT może być używane wielokrotnie. W naszym przypadku przeniesie nas już na poziom ALL MEMBER MEASURES.ZyskKategorii AS ([KategoriaProduktuH].CURRENTMEMBER.PARENT. PARENT, [Measures].[Zysk]) MEMBER MEASURES.ZyskProcentowy AS ([KategoriaProduktuH].CURRENTMEMBER, [Measures].[Zysk])/ ([KategoriaProduktuH].CURRENTMEMBER.PARENT.PARENT, [Measures].[Zysk]), FORMAT_STRING = '0.00%' {Measures.ZyskKategorii, [Measures].[Zysk], MEASURES.ZyskProcentowy} ON COLUMNS, [KategoriaProduktuH].[Nazwa Towaru] ON ROWS użycie nazwanych zbiorów i funkcji EXCEPT (odnajduje różnice pomiędzy dwoma zbiorami) umożliwia skonstruowanie wyrażenia, które pokaże procentową sprzedaż dla każdej grupy towarów w porównaniu do całości pomniejszonej o sprzedaż zegarów SET [OproczZegarow] AS EXCEPT([KategoriaProduktuH].[Nazwa Kategorii].MEMBERS, [Towar G].[KategoriaProduktuH].[Nazwa Kategorii].[Clocks]) MEMBER Measures.ProcentSprzedazy AS ([KategoriaProduktuH].CURRENTMEMBER, [Measures].[Zysk]) / SUM([OproczZegarow], [Measures].[Zysk]), FORMAT_STRING = '#.00%' {[Measures].[Zysk], Measures.ProcentSprzedazy} ON COLUMNS, [OproczZegarow] ON ROWS 11
Formatowanie warunkowe SET Towary as [KategoriaProduktuH].[Nazwa Kategorii].MEMBERS MEMBER Measures.zakup AS [Measures].[Wartosc], FORMAT_STRING = IIF(zakup>800,'#.00','{#.00)'), fore_color = IIF(zakup>800,RGB(255,0,0), RGB(255,255,0)), back_color = IIF(zakup>800,RGB(0,255,255), RGB(0,155,0)), FONT_FLAGS = IIF(zakup>800,MDFF_BOLD or MDFF_UNDERLINE,MDFF_ITALIC or MDFF_STRIKEOUT), FONT_NAME = IIF(zakup>800,ARIAL,Times), FONT_SIZE = IIF(zakup>800,16,10) Towary ON COLUMNS, [LokalizacjaKlientaH].[Województwo].MEMBERS ON ROWS Nie wszystko działa?!! WHERE Measures.Zakup CELL PROPERTIES VALUE, FORMATTED_VALUE, CELL_ORDINAL, FORMAT_STRING, FORE_COLOR, BACK_COLOR, FONT_FLAGS, FONT_NAME, FONT_SIZE To samo ale z zastosowaniem DESCENDANTS GENERATE([DataFakturyH].[Rok].MEMBERS, {[DataFakturyH].CURRENTMEMBER, DESCENDANTS([DataFakturyH].CURRENTMEMBER, [Data Faktury].[DataFakturyH].[Kwartal])}) ON COLUMNS, [KategoriaProduktuH].[Nazwa Kategorii].members ON ROWS WHERE ([Measures].[Zysk]) Rozpatrzmy zapytanie mające zwracać na osi kolumn informacje o sprzedaży w każdym roku oraz odpowiadające im szczegóły kwartalne. GENERATE([DataFakturyH].[Rok].MEMBERS, {[DataFakturyH].CURRENTMEMBER, [DataFakturyH].CURRENTMEMBER.CHILDREN}) ON COLUMNS, [KategoriaProduktuH].[Nazwa Kategorii].members ON ROWS WHERE ([Measures].[Zysk]) To samo ale z rozbiciem na miesiące GENERATE([DataFakturyH].[Rok].MEMBERS, {[DataFakturyH].CURRENTMEMBER, DESCENDANTS([DataFakturyH].CURRENTMEMBER, [Data Faktury].[DataFakturyH].[Miesiac])}) ON COLUMNS, [KategoriaProduktuH].[Nazwa Kategorii].members ON ROWS WHERE ([Measures].[Zysk]) 12
Pokazanie wzrostu w okresie czasu umożliwia funkcja PREVMEMBER. Jeżeli mielibyśmy wyświetlić zysk ze sprzedaży i przyrostową zmianę od poprzedniego członu czasu na poziomie kolejnych miesięcy MEMBER Measures.[wzrost Zysku] AS ([Measures].[Zysk]) - ([Measures].[Zysk],[DataFakturyH].PREVMEMBER), FORMAT_STRING = '###,###.00 ZŁ' {[Measures].[Zysk], Measures.[wzrost Zysku]} ON COLUMNS, {DESCENDANTS([DataFakturyH], [DataFakturyH].[miesiac])} ON ROWS Możemy również użyć funkcji LEAD, która zwraca człon oddalony w wymiarze o określoną liczbę pozycji od wskazanego członu. MEMBER Measures.[wzrost Zysku] AS ([Measures].[Zysk]) - ([Measures].[Zysk],[DataFakturyH]. LEAD)(-2)), FORMAT_STRING = '###,###.00 ZŁ' {[Measures].[Zysk], Measures.[wzrost Zysku]} ON COLUMNS, {DESCENDANTS([DataFakturyH], [DataFakturyH].[miesiac])} ON ROWS Użycie NEXTMEMBER w tym wyrażeniu pokazałoby sprzedaż dla każdego miesiąca zestawioną ze sprzedażami z poprzednich miesięcy. MEMBER Measures.[wzrost Zysku] AS ([Measures].[Zysk]) - ([Measures].[Zysk],[DataFakturyH]. NEXTMEMBER), FORMAT_STRING = '###,###.00 ZŁ' {[Measures].[Zysk], Measures.[wzrost Zysku]} ON COLUMNS, {DESCENDANTS([DataFakturyH], [DataFakturyH].[miesiac])} ON ROWS PARALLELPERIOD pozwala na łatwe porównanie wzrostu ze wzrostem z tego samego przedziału czasu w poprzednim kwartale: MEMBER Measures.[wzrost Zysku] AS ([Measures].[Zysk]) - (Measures.[Zysk], PARALLELPERIOD([DataFakturyH].[Kwartal])), FORMAT_STRING = '###,###.00 ZŁ' {[Measures].[Zysk], Measures.[wzrost Zysku]} ON COLUMNS, {DESCENDANTS([DataFakturyH], [DataFakturyH].[miesiac])} ON ROWS Pojawiają się błędy gdy nie zdefiniowno równoległego miesiąca. Miesiące w obrębie kwartału są wykrywane jako kolejne wystąpienia na liście, niezgodnie z kalendarzem 13
Można sprawdzić o ile wzrosła sprzedaż po pierwszym miesiącu sezonu. Używając kwartału do przedstawienia sezonu, można mierzyć różnicę w sprzedaży jednostek dla każdego miesiąca, porównując z miesiącem otwierającym kwartał: MEMBER Measures.[wzrost Zysku] AS ([Measures].[Zysk]) - (Measures.[Zysk], OPENINGPERIOD([DataFakturyH].Miesiac, [DataFakturyH].CURRENTMEMBER.PARENT)), FORMAT_STRING = '###,###.00 ZŁ' {[Measures].[Zysk], Measures.[wzrost Zysku]} ON COLUMNS, {DESCENDANTS([DataFakturyH], [DataFakturyH].[miesiac])} ON ROWS W postaci skróconej można zastosować funkcję YTD() MEMBER Measures.Zysk_YTD AS SUM(YTD(), [Measures].[Zysk]), FORMAT_STRING = '#.00' {[KategoriaTowaruG].[Nazwa Kategorii].MEMBERS} ON Columns, {DESCENDANTS([DataFakturyH], [DataFakturyH].Miesiac)} ON rows WHERE (Measures.Zysk_YTD) OPENINGPERIOD(poziom, człon) CLOSINGINGPERIOD(poziom, człon) Oprócz YTD() istnieją QTD(), MTD() i WTD(). Przy użyciu funkcji SUM i PERIODSTODATE można zdefiniować obliczany człon, który wyświetli informację year-to-date.. Miarą jest suma bieżąca na poziomie roku: PERIODSTODATE([Time].[Year], [Time].CURRENTMEMBER) MEMBER Measures.Zysk_YTD AS SUM(PERIODSTODATE([DataFakturyH].[Rok],[DataFakturyH].CURRENTMEMBER), [Measures].[Zysk]), FORMAT_STRING = '#.00' {[KategoriaTowaruG].[Nazwa Kategorii].MEMBERS} ON Columns, {DESCENDANTS([DataFakturyH], [DataFakturyH].miesiac)} ON rows WHERE (Measures.Zysk_YTD) Przy użyciu funkcji SUM i PERIODSTODATE można zdefiniować obliczany człon, który wyświetli informację Quarter-to-date.. Miarą jest suma bieżąca na poziomie Kwartału: MEMBER Measures.Zysk_QTD AS SUM(PERIODSTODATE([DataFakturyH].[Kwartal],[DataFakturyH].CURRENTMEMB ER), [Measures].[Zysk]), FORMAT_STRING = '#.00' {[KategoriaTowaruG].[Nazwa Kategorii].MEMBERS} ON Columns, {DESCENDANTS([DataFakturyH], [DataFakturyH].miesiac)} ON rows WHERE (Measures.Zysk_QTD) 14
To samo przy użyciu funkcji QTD: MEMBER Measures.Zysk_QTD AS SUM(QTD(), [Measures].[Zysk]), FORMAT_STRING = '#.00' {[KategoriaTowaruG].[Nazwa Kategorii].MEMBERS} ON Columns, [DataFakturyH].miesiac ON rows WHERE (Measures.Zysk_QTD) Usuwanie pustych członów z osi możemy osiągnąć przez zastosowanie klauzuli NON EMPTY, [KategoriaTowaruG].[Nazwa Kategorii].MEMBERS ON COLUMNS, NON EMPTY{CROSSJOIN( [Klienci G].[Województwo].MEMBERS, [Data Faktury].[Kwartal].MEMBERS)} ON ROWS WHERE ([Measures].[Zysk]) Przy takiej definicji krotki zapytanie bieżące i z poprzedniej strony zwracają to samo W wielu przypadkach kombinacja członów z różnych wymiarów jest zamknięta w nawiasach. Kombinacja taka znana jest jako krotka (tuplet) i jest wykorzystywana do wyświetlenia wielu wymiarów na jednej osi. [KategoriaTowaruG].[Nazwa Kategorii].MEMBERS ON COLUMNS, {CROSSJOIN( [Klienci G].[Województwo].MEMBERS, [Data Faktury].[Kwartal].MEMBERS)} ON ROWS WHERE ([Measures].[Zysk]) Do filtrowania bardziej szczegółowego, MDX udostępnia funkcję FILTER. Zwraca ona zbiór, który jest wynikiem filtrowania na podstawie określonego warunku. FILTER (zbiór, warunek_wyszukiwania) [KategoriaTowaruG].[Nazwa Kategorii].MEMBERS ON COLUMNS, FILTER({[LokalizacjaKlientaH].[Miasto].MEMBERS}, ([Measures].[Zysk], [DataFakturyH].[All])>500) ON ROWS WHERE ([Measures].[Zysk]) 15
Do filtrowania bardziej szczegółowego, MDX udostępnia funkcję FILTER. Zwraca ona zbiór, który jest wynikiem filtrowania na podstawie określonego warunku. FILTER (zbiór, warunek_wyszukiwania) [KategoriaTowaruG].[Nazwa Kategorii].MEMBERS ON COLUMNS, FILTER({[LokalizacjaKlientaH].[Miasto].MEMBERS}, ([Measures].[Zysk], [DataFakturyH].[All])>500) ON ROWS WHERE ([Measures].[Zysk], [DataFakturyH].[Rok].[2003]) Zysk których z produktów spada poniżej średniej dla województwa????? MEMBER [Measures].[ZyskProcentowy] AS ([Measures].[Wartosc]-[Measures].[WartoscZakupu]) / ([Measures].[WartoscZakupu]), FORMAT_STRING = '#.00%' NON EMPTY {[KategoriaTowaruG].[Nazwa Kategorii].MEMBERS} ON COLUMNS, FILTER({[LokalizacjaKlientaH].[Miasto].MEMBERS}, ([Measures].[ZyskProcentowy], [DataFakturyH].[All]) < ([Measures].[ZyskProcentowy], [DataFakturyH].[All], ANCESTOR([LokalizacjaKlientaH].CURRENTMEMBER, [LokalizacjaKlientaH].[Województwo]))) ON ROWS WHERE ([Measures].[ZyskProcentowy], [DataFakturyH].[All]) Filtr dla zysku we wszystkich latach większego niż 500 ale wyświetlono zysk z roku 2003 Jest sens stosowanie NON EMPTY wyeliminowano PABIANICE [KategoriaTowaruG].[Nazwa Kategorii].MEMBERS ON COLUMNS, NON EMPTY(FILTER({[LokalizacjaKlientaH].[Miasto].MEMBERS}, ([Measures].[Zysk], [DataFakturyH].[All])>500)) ON ROWS WHERE ([Measures].[Zysk], [DataFakturyH].[Rok].[2003]) Rozpatrzmy proste zapytanie o miary dla lokalizacji klientów (miasta): [Measures].MEMBERS ON COLUMNS, [LokalizacjaKlientaH].[Miasto].MEMBERS ON ROWS Filtr dla zysku we wszystkich latach większego niż 500 ale wyświetlono zysk z roku 2003 16
Pierwszym wyświetlonym miastem jest Bydgoszcz etc... Naturalna kolejność nie jest zbyt widoczna ponieważ nie znamy członu z poziomu rodzica. Jeśli bylibyśmy zainteresowani miastami wylistowanymi między Kutno a Czersk musielibyśmy napisać: [Measures].MEMBERS ON COLUMNS, [LokalizacjaKlientaH].[Miasto].[KUTNO]:[CZERSK] ON ROWS Sortowanie wyników ORDER(zbiór, wyrażenie [, ASC DESC BASC BDESC]) Brak prefiksu B wskazuje, że hierarchiczny porządek nie może zostać rozbity. [Measures].MEMBERS ON COLUMNS, ORDER([LokalizacjaKlientaH].[Miasto].[KUTNO]:[CZERSK], [LokalizacjaKlientaH].CURRENTMEMBER.NAME, ASC) ON ROWS Sortowanie wyników ORDER(zbiór, wyrażenie [, ASC DESC BASC BDESC]) Prefiks B wskazuje, że hierarchiczny porządek może zostać rozbity. [Measures].MEMBERS ON COLUMNS, ORDER([LokalizacjaKlientaH].[Miasto].[KUTNO]:[CZERSK], [LokalizacjaKlientaH].CURRENTMEMBER.NAME, BASC) ON ROWS Dość często bieżące sortowanie jest oparte o aktualną miarę. Zapytamy o informacje sprzedaży w miastach, uporządkowaną według efektywności sprzedaży: [Measures].MEMBERS ON COLUMNS, ORDER([LokalizacjaKlientaH].[Miasto].MEMBERS, [Measures].[Wartosc], DESC) ON ROWS W przykładzie tym sortowanie odbyło się po właściwości Name. Zwraca on nazwę poziomu, wymiaru, członu, lub hierarchii. Istnieje podobna właściwość UniqueName zwracająca odpowiednią unikalną nazwę. 17
Dość często bieżące sortowanie jest oparte o aktualną miarę. Zapytamy o informacje sprzedaży w miastach, uporządkowaną według efektywności sprzedaży: [Measures].MEMBERS ON COLUMNS, ORDER([LokalizacjaKlientaH].[Miasto].MEMBERS, [Measures].[Wartosc], BDESC) ON ROWS TOPCOUNT zwraca ze zbioru n najlepszych na podstawie podanego wyrażenia wyników. TOPCOUNT(zbiór, n, wyrażenie_numeryczne) Poprzednie wyrażenie możemy zatem przepisać: [Measures].MEMBERS ON COLUMNS, TOPCOUNT([LokalizacjaKlientaH].[Miasto].MEMBERS, 10, [Measures].[Wartosc]) ON ROWS Bez rozbicia wynikającego z hierarchii HEAD funkcja zwracająca pierwszych n członów z podanego zbioru. Podobnie TAIL zwraca n ostatnich członów z podanego zbioru. Ograniczmy zakres wyświetlanych miast do 10 najbardziej wydajnych: [Measures].MEMBERS ON COLUMNS, HEAD(ORDER([LokalizacjaKlientaH].[Miasto].MEMBERS, [Measures].[Wartosc], BDESC), 10) ON ROWS Zapytanie MDX, które wyświetla górne 10 miast, w oparciu o ilość transakcji sprzedaży, oraz jak dużo sprzedały łącznie pozostałe miasta. Takie wyrażenie pokaże także inne zastosowanie funkcji SUM, nawiązujące do nazwanych zbiorów i obliczanych członów: SET TopTens AS TOPCOUNT([LokalizacjaKlientaH].[Miasto].MEMBERS, 10, [Measures].[Ilosc]) MEMBER [LokalizacjaKlientaH].[Inne Miasta] AS ([LokalizacjaKlientaH].[All], Measures.CURRENTMEMBER) - SUM(TopTens, Measures.CURRENTMEMBER) [Measures].MEMBERS ON COLUMNS, {TopTens, [Inne Miasta]} ON ROWS Istnieje również TOPPERCENT, TOPSUM Oczywiście istnieje seria funkcji BOTTOM 18
Wyświetlanie listy miast, których ilość transakcji sprzedaży obejmuje 50% całości sprzedaży: [Measures].MEMBERS ON COLUMNS, TOPPERCENT({[Klienci G].[LokalizacjaKlientaH].[Miasto].MEMBERS}, 50, [Measures].[Ilosc]) ON ROWS rozkład ilości sprzedaży dla typów sklepów: [Nazwa Kategorii].MEMBERS ON COLUMNS, TOPPERCENT({[LokalizacjaKlientaH].[Miasto].MEMBERS}, 50, [Measures].[Ilosc]) ON ROWS WHERE [Measures].[Ilosc] Wyświetlanie listy 50% osób, których zarobki w roku 2004 były najwyższe Zastosowanie TOPSUM TOPPERCENT ([Osoby G].[Pracownik].[Pracownik].members, 50, ([Measures].[Brutto], [Data Zarobki].[Rok].[2004])) ON ROWS, {[Data Zarobki].[Rok].[2004]} ON COLUMNS FROM ZarobkiG MEMBER [Data Faktury].[Rok].[2002 + 2003] AS ([Measures].[Wartosc], [Data Faktury].[Rok].[2002]) + ([Measures].[Wartosc], [Data Faktury].[Rok].[2003]) {[Data Faktury].[Rok].[2002], [Data Faktury].[Rok].[2003], [Data Faktury].[Rok].[2002 + 2003]} ON COLUMNS, TOPSUM([Towar G].[Nazwa Towaru].[Nazwa Towaru].members, 10000, [Data Faktury].[Rok].[2002 + 2003]) ON ROWS FROM BiznesG WHERE [Measures].[Wartosc] 19
Funkcje statystyczne AVG, MEDIAN, MAX, MIN, VAR oraz STDDEV. Format jest jednakowy dla wszystkich: FUNKCJA(zbiór, wyrażenie_numeryczne) MEMBER [DataFakturyH].[SredniaSprzedaz] AS AVG(DESCENDANTS([DataFakturyH].[Rok], [DataFakturyH].[Miesiac])) MEMBER [DataFakturyH].[SredniaIlosc] AS COUNT(DESCENDANTS([DataFakturyH].[Rok], [DataFakturyH].[Miesiac]),EXCLUDEEMPTY) {[DataFakturyH].[Rok],[SredniaSprzedaz],[SredniaIlosc]} ON COLUMNS, [KategoriaTowaruG].[Nazwa Kategorii] ON ROWS WHERE [Measures].[Ilosc] Jeśli zapytanie ma zwracać wzrost procentowy MEMBER Measures.[WzrostZysku] AS ([Measures].[Zysk]) / ([Measures].[Zysk], [DataFakturyH].PREVMEMBER), FORMAT_STRING = '#.00%' {[Measures].[Zysk], [WzrostZysku] } ON COLUMNS, DESCENDANTS([DataFakturyH].[Rok], [DataFakturyH].[Miesiac]) ON ROWS Problemem - pierwszy okres czasu powoduje dzielenie przez zero... Funkcje te, zwane user-defined functions (UDF) mogą przyjmować argumenty i zwracać wartości w składni MDX. UDF mogą być tworzone w dowolnym języku wspomagającym COM (Common Object Model), Na dodatek, MDX obsługuje wiele funkcji z biblioteki Microsoft Visual Basic for Applications (VBA) Expression Services. Biblioteka ta jest dołączona do OLAP Services i jest automatycznie rejestrowana. Możemy zapytać o miary dla miasta, gdzie wartość zawiera łańcuch KA : Measures.MEMBERS ON COLUMNS, FILTER({[LokalizacjaKlientaH].[Miasto].MEMBERS}, VBA!INSTR(1, [Miasto].CURRENTMEMBER.Name, "KA")>0) ON ROWS Ten problem może zostać w omnięty przez użycie IIF i sprawdzenie istnienia pustej komórki: MEMBER Measures.[WzrostZysku] AS IIF( ISEMPTY([DataFakturyH].PREVMEMBER), 1, ([Measures].[Zysk]) / ([Measures].[Zysk], [DataFakturyH].PREVMEMBER)), FORMAT_STRING = '#.00%' {[Measures].[Zysk], [WzrostZysku] } ON COLUMNS, DESCENDANTS([DataFakturyH].[Rok], [DataFakturyH].[Miesiac]) ON ROWS Prefiks VBA nie jest potrzebny. Wskazuje jedynie pełnokwalifikowane pochodzenie funkcji. 20
Taka sama funkcjonalność może być osiągnięta przy użyciu funkcji COALESCEEMPTY, która przypisuje pustej wartości komórki liczbę lub łańcuch i go zwraca. W tym przypadku, pusta komórka z poprzedniego członu czasu, byłaby zastąpiona wartością bieżącym członem czasu: MEMBER Measures.[WzrostZysku] AS ([Measures].[Zysk]) / COALESCEEMPTY(([Measures].[Zysk], [DataFakturyH].PREVMEMBER),[Measures].[Zysk]), FORMAT_STRING = '#.00%' {[Measures].[Zysk], [WzrostZysku] } ON COLUMNS, DESCENDANTS([DataFakturyH].[Rok], [DataFakturyH].[Miesiac]) ON ROWS Średnia bieżąca - CENTRALNA MEMBER Measures.SredniaBiezaca AS AVG({[Miesiac].CURRENTMEMBER, [Miesiac].PREVMEMBER, [Miesiac].NEXTMEMBER},[Measures].[Wartosc]), FORMAT_STRING = '#.00 zł' [KategoriaProduktuH].[Nazwa Kategorii].[THAN] ON COLUMNS, [DataFakturyH].[Miesiac].MEMBERS ON ROWS WHERE Measures.SredniaBiezaca Średnia dla miast dla wybranych województw za lata 2001 do 2004 MEMBER [Measures].[lubelskie AVG] AS AVG({[Klienci G].[lubelskie].CHILDREN},[Measures].[Wartosc]) MEMBER [Measures].[łódzkie AVG] AS AVG({[Klienci G].[łódzkie].CHILDREN},[Measures].[Wartosc]) MEMBER [Measures].[pomorskie AVG] AS AVG({[Klienci G].[pomorskie].CHILDREN},[Measures].[Wartosc]) {([Measures].[lubelskie AVG]), ([Measures].[łódzkie AVG]), ([Measures].[pomorskie AVG])} ON COLUMNS, {([Data Faktury].[2001]:[2003])} ON ROWS FROM BiznesG Średnia bieżąca - WSTECZNA MEMBER Measures.SredniaBiezaca AS AVG({[Miesiac].CURRENTMEMBER, [Miesiac].PREVMEMBER, [Miesiac].PREVMEMBER.PREVMEMBER},[Measures].[Wartosc]), FORMAT_STRING = '#.00 zł' [KategoriaProduktuH].[Nazwa Kategorii].[THAN] ON COLUMNS, [DataFakturyH].[Miesiac].MEMBERS ON ROWS WHERE Measures.SredniaBiezaca 21