Podstawy języka MDX Tworzenie zbiorów
Używanie zbiorów Zbiór to: wynik działania funkcji (np. funkcji members) lista elementów otoczona {...} {[Store Sales], [Unit Sales]} on columns, [Product].[Prod].[Category].members [ ] [ on rows from test where ([1998].[Quarter [ 1])
Używanie zbiorów Zbiór może zawierać tylko jeden element trzeba jednak pamiętać o nawiasach klamrowych! { [Unit Sales] } on columns, [Product].[Prod].[Category].members on rows from test where ([1998].[Quarter 1])
Funkcje zwracające zbiór members dla poziomu: [Customer].[Cust].[Country].members dla całego ł wymiaru: children [Customer].[Cust].members dla konkretnego elementu: [Customer].[Cust].[USA].children ] [USA] [Customer].[Cust].[Richmond].children
Members a children Dla member'a: [customer]. [Cust].[Burnaby].children ok [customer]. [Cust].[Burnaby].members błąd! Dla poziomu: [customer]. [Cust].[City].members ok [customer]. [Cust].[City].children błąd!
Podobne funkcje Dla members: allmembers addcalculatedmembers stripcalculatedmembers Dla children siblings rodzeństwo aktualnego elementu parent podaje rodzica (i to nie jest zbiór!)
Funkcja Descendants Analogiczna do ancestor Descendants(<member>,<level>[,...]) <level> to nazwa poziomu lub numer Bez podania poziomu zwraca wszystkich potomków elementu Opcjonalny trzeci parametr to flaga, która mówi które poziomy zwrócić
Flagi Descendants SELF domyślne, tylko podany poziom AFTER niższe od podanego poziomu BEFORE wszystkie pomiędzy elementem i podanym poziomem (bez tego poziomu!) SELF_AND_AFTER podany poziom i niższe SELF_AND_BEFORE - wszystkie pomiędzy ę elementem i podanym poziomem oraz podany poziom LEAVES wszystkie liście elementy bez potomków pomiędzy podanym elementem a podanym poziomem
Przykład dla Descendants Wszystkie miasta USA Descendants([customer]. [Cust].[USA],[USA] [City]) Wszyscy potomkowie USA Descendants([customer]. [Cust].[USA])[USA]) Wszystkie nazwiska (liście) z USA Descendants([customer]. [Cust].[USA],, LEAVES) Wszystkie stany USA Descendants([customer]. [Cust].[USA], [ ], [City], BEFORE) Wszystkie stany i miasta USA Descendants([customer]. [Cust].[USA], [City], SELF_AND_BEFORE)
Manipulacje zbiorami Funkcje: Head i Tail Union Intersect t Except Hierarchize
Funkcja Head Zwraca X pierwszych wyników [Customer]. [Cust].[country].[USA].children[country] [USA] children on columns, [Product].[Prod].[Category].members on rows
Funkcja Head Zwraca X pierwszych wyników [Customer]. [Cust].[country].[USA].children[country] [USA] children on columns, Head( [Product].[Prod].[Category].members, [ ] [ 5 ) on rows
Funkcja Tail Zwraca X ostatnich wyników [Customer].[Cust].[country].[USA].children[Cust] [country] [USA] children on columns, Tail( [Product].[Prod].[Category].members, [ ] [ 5 ) on rows
UNION - tworzenie unii Łączenie dwóch zbiorów [Customer].[Cust].[country].[USA].children[Cust] [country] [USA] children on columns, Union( [Time].[Tm].[Year].members, ) on rows [Time].[Tm].[Month].members [ ] [ ]
UNION Łączone zbiory muszą pochodzić z tego samego wymiaru! [Customer].[Cust].[country].[USA].children on columns, Union( [Time].[Tm].[Year].members, [ ] [ ] [Product].members ) on rows
UNION Można wpisać zbiory w {...} [Customer].[Cust].[country].[USA].children[Cust] [country] [USA] children on columns, Union( {[styczeń],[luty]}, {[1998]} ) on rows
UNION Można wpisać zbiory w {...} [Customer].[Cust].[country].[USA].children[Cust] [country] [USA] children on columns, Union( {[1998].[Quarter 1].[styczeń], [1998].[Quarter [ 1].[luty]}, {[1998]} ) on rows
UNION Można wpisać zbiory w {...} [Customer].[Cust].[country].[USA].children[Cust] [country] [USA] children on columns, Union( {[1998].&[1].[styczeń], [1998].&[1].[luty]}, [ ] [ {[1998]} ) on rows
UNION Ale można wtedy pominąć Union [Customer].[Cust].[country].[USA].children[Cust] [country] [USA] children on columns, {[1998].&[1].[styczeń], [1998].&[1].[luty], [1998]} on rows
UNION Użycie UNION automatycznie usuwa duplikaty (elementy powtarzające się w obu zbiorach) Zwykłe połączenie zbiorów nie usuwa duplikatów Można użyć: UNION(<set1>, <set2>, ALL) Wtedy duplikaty nie zostaną usunięte
Kolejność elementów Zapytanie zwraca najpierw lata a potem kwartały to nieczytelne! [Customer].[Cust].[country].[USA].children on columns, Union( ([Time].[Year].members, ][ ] [Time].[Quarter].members ) on rows
Funkcja Hierarchize Układa elementy zgodnie z hierarchią [Customer].[Cust].[country].[USA].children[Cust] [country] [USA] children on columns, Hierarchize( Union( ([Time].[Year].members, ][ ] [Time].[Quarter].members )) on rows
Funkcja Except Except( <set1>, <set2>) Usuwa ze zbioru pierwszego elementy znajdujące j się także w zbiorze drugim [Product].[Prod].[Category].members on columns, Except( [Customer].[Cust].[USA].[CA].children, {[Customer].[Cust].[San Francisco]} ) on rows
Funkcja Except Except( <set1>, <set2>) Usuwa ze zbioru pierwszego elementy znajdujące j się także w zbiorze drugim [Product].[Prod].[Category].members on columns, Except( [Customer].[Cust].[USA].[CA].children, {[Customer].[Cust].[San Francisco], [Customer].[Cust].[Santa Cruz]} ) on rows
Operator zakresu Działa jak w MS Excel Dla M1:M2 M2 zbiór zawiera elementy od M1 do M2 [Product].[Prod].[Category].members on columns, Except( [Customer].[Cust].[USA].[CA].children, {[Customer].[Cust].[San Francisco] : [Customer].[Cust].[Santa Cruz]} ) on rows
Funkcja Intersect Intersect(<set1>, <set2>) Zwraca część wspólną zbiorów [Product].[Prod].[Category].members on columns, Intersect( [Customer].[Cust].[Mexico].[DF].children, ) on rows Head([Customer].[Cust].[City].members,20)
Funkcja Distinct Distinct( <set>) Usuwa duplikaty ze zbioru [Product].[Prod].[Category].members on columns, { [Customer].[Cust].[Mexico].[DF].children, [ ] [ ] [ ] on rows { [Customer].[Cust].[San Andres] } }
Funkcja Distinct Distinct( <set>) Przykład miasta nie pojawią się dwa razy [Product].[Prod].[Category].members on columns, Distinct( {[Customer].[Cust].[Mexico].[DF].children, ) on rows { [Customer].[Cust].[San Andres] }}
Podstawy języka MDX Podzapytania
Podzapytania Do stworzenia niektórych raportów MDX tworzy najpierw niewidoczne zapytanie, a potem dopiero na jego podstawie przygotowuje wynik Podzapytania MDX i podzapytania SQL to coś zupełnie innego!
Tematyka modułu Funkcje tworzące podzapytania Order sortowanie danych TopCount tworzenie rankingów Filter filtrowanie na osiach
Funkcja Order Order(<set>, <wartość> [,<flaga>]) Sortuje podany zbiór {[Store Sales], [Unit Sales]} on columns, Order( [Customer].[Mexico].children, [Store Sales] ) on rows
Funkcja Order Flagi: ASC (domyślna), DESC, BASC, BDESC {[Store Sales], [Unit Sales]} on columns, Order( [Customer].[Mexico].children, [Store Sales], ASC )on rows
Funkcja Order Flagi: ASC (domyślna), DESC, BASC, BDESC {[Store Sales], [Unit Sales]} on columns, Order( [Customer].[Mexico].children, [Store Sales], DESC )on rows
Funkcja Order ASC i DESC zachowują hierarchię (sortują według poziomów) {[Store Sales], [Unit Sales]} on columns, Order( [Customer].[State Province].members, [Store Sales] )on rows
Funkcja Order ASC i DESC zachowują hierarchię (sortują według poziomów) {[Store Sales], [Unit Sales]} on columns, Order( {[Customer].[State Province].members, [Customer].[Country].members}, [Store Sales] )on rows
Funkcja Order BASC i BDESC sortują bezpośrednio według wartości {[Store Sales], [Unit Sales]} on columns, Order( {[Customer].[State Province].members, [Customer].[Country].members}, [Store Sales], BASC )on rows
Sortowanie alfabetyczne Jako wartości sortowanej używamy nazwy elementu {[Store Sales], [Unit Sales]} on columns, Order( [Customer].[State Province].members, [customer].currentmember.name )on rows
Sortowanie alfabetyczne Należy dodać BASC lub BDESC aby sortować niezaleznie od hierarchii {[Store Sales], [Unit Sales]} on columns, Order( [Customer].[State Province].members, [customer].currentmember.name, BASC )on rows
Ranking najlepszych Podaj pięć miast z największą sprzedażą {[Store Sales]} on columns, Head( Order([Customer].[City].members, [Store Sales], DESC), 5) on rows
Ranking najlepszych Podaj pięć miast z największą sprzedażą {[Store Sales]} on columns, TopCount( [Customer].[City].members, 5, [Store Sales] ) on rows
Inne rankingi BottomCount TopSum BottomSum TopPercent BottomPercent
Filtrowanie osi Fukcja Filter(<set>, <condition>) {[Store Sales]} on columns, Filter( [Customer].[City].members, [Store Sales]>50000 ) on rows
Funkcja Filter Nakłada warunki na elementy osi Filtry niezależne dla każdej osi (kolumny/wiersza) Drugi parametr jest dowolnym wyrażeniem warunkowym {[Store Sales]} on columns, Filter( [Customer].[City].members, [customer].currentmember.name>"van" ) on rows
Funkcja Filter Filtr działa dla elementów wymiaru pokazanego na danej osi niezależnie od drugiej osi Żadna komórka nie ma wartości > 50000! [Product].[product category].members on columns, Filter( [Customer].[City].members, [Store Sales]>50000 ) on rows
Funkcja Filter Filtr działa dla elementów wymiaru pokazanego na danej osi niezależnie od drugiej osi Suma komórek jest dla danego wiersza jest > 50000 { [Product].[All Product], [Product].[product category].members} on columns, Filter( [Customer].[City].members, [Store Sales]>50000 ) on rows
Filter a Where Klauzula WHERE na końcu zapytania dotyczy innych wymiarów nie tych na osiach! {[Store Sales]} on columns, Filter( [Customer].[City].members, [Store Sales]>500 ) on rows
Filter a Where Podaje i oblicza [Store Sales] tylko dla wina i piwa {[Store Sales]} on columns, Filter( [Customer].[City].members, [Store Sales]>500 ) on rows where [Product].[Beer and Wine]
Filter a Where Generuje błąd! {[Store Sales]} on columns, Filter( [Customer].[City].members, [Store Sales]>500 ) on rows where [Customer].[USA]
NON EMPTY Modyfikator usuwający puste elementy osi (np. pusty wiersz lub pustą kolumnę) Nie jest to funkcja wynik zależy od wszystkich osi a nie tylko tej na której zastosowano modyfikator!
Użycie NONEMPTY W bazie brak danych z 1997 roku, więc połowa wierszy jest pusta {[Store Sales],[Store Cost]} on columns, [Time].[Quarter].members[Q members on rows from test
Użycie NONEMPTY Rozwiązanie: użycie modyfikatora NON EMPTY na wierszach {[Store Sales],[Store Cost]} on columns, NON EMPTY [Time].[Quarter].members[Q members on rows from test
Raporty 3-wymiarowe Przykład: Raport pokazujący sprzedaż kategorii produktów (1) w jednostkach czasu (2) w różnych lokalizacjach (3) Raport trzeba zwizualizować w postaci dwuwymiarowej Jest to możliwe w Cube Browser'ze W języku MDX służy do tego funkcja Crossjoin
Raport 3 wymiarowy Raport przedstawiający sprzedaż produktów w krajach [Product].[product [ d t category].members on columns, [country].members on rows Jak do tego dodać informację o sprzedaży w kwartałach?
Raport 3 wymiarowy Raport przedstawiający sprzedaż produktów w krajach z podziałem na kwartały [Product].[Product Category].members on columns, crossjoin([country].members, ntr [Quarter].members) on rows
Raport 3 wymiarowy Dodanie informacji o latach [Product].[Product t][p d t Category].members on columns, crossjoin([country].members, {[Quarter].members, [Year].members}) on rows
Raport 3 wymiarowy Ułożenie wierszy wg hierarchii [Product].[Product t][p d tcategory].members on columns, hierarchize( crossjoin([country].members, {[Quarter].members, [Year].members}) ) on rows
Raport 3 wymiarowy Zamiana kolejności [Product].[Product Category].members on columns, hierarchize( crossjoin( {[Quarter].members, [Year].members}, [Country].members) ) on rows
Podstawy języka MDX Agregacje g
Tworzenie elementów Dynamiczne tworzenie elementów wyliczanych (calculated members) with member <nazwa> as '<definicja>' [,<format_string>]...
Tworzenie elementów Podaj średnią cenę każdego produktu with member Measures.[Average Price] as '[Store Sales]/[Unit Sales]' {[Store Sales], [Unit Sales], [Average Price]} on columns [Product].[Product t][p d tn Name].members on rows
Funkcje agregujące Parametry zbiór wielkość obliczana (miara) Wyjście pojedyncza wartość Przykład sum([usa].children, [Store Sales]) Suma sprzedaży dla wszystkich stanów USA
Tworzenie agregacji Podaj sumaryczną sprzedaż dla każdego produktu z kategorii [Bread] with member Measures.[Sum Store] as 'Sum([Product].CurrentMember.children, [Store Sales])' {[Store Sales], [Unit Sales], [Sum Store]} on columns, Descendants([Product].[Bread]) d [B d]) on rows
Średnia sprzedaż Podaj średnią sprzedaż dla produktów z kategorii [Bread] with member Measures.[Sum Store] as 'Avg([Product].CurrentMember.children, [Store Sales])' {[Store Sales], [Unit Sales], [Sum Store]} on columns, Descendants([Product].[Bread]) d [B d]) on rows
Średnia cena Podaj ceny dla produktów z kategorii [Bread] with member Measures.[Price] as '[Store Sales]/[Unit Sales]' {[Store Sales], [Unit Sales], [Price]} on columns, Descendants([Product].[Bread]) on rows
Średnia cena Podaj średnią cenę dla produktów z kategorii [Bread] with member Measures.[Price] as '[Store Sales] / [Unit Sales]' member Measures.[Avg Price] as 'Avg([Product].CurrentMember.children, [Price])' {[Store Sales], [Unit Sales], [Price], [Avg Price]} on columns, Descendants([Product].[Bread]) on rows
Różne funkcje agregujące Sum Min Max Avg Count DistinctCount Median Variance Covariance
Przykładowa statystyka Podaj o ile gorsza jest sprzedaż danego produktu względem najlepszego w jego kategorii Sprzedaż najlepszego: TopCount([Bread].children, 1, [Store Sales]) Problem: zwraca zbiór (jednoelementowy!) Nie można więc napisać: [Store Sales] - TopCount([Bread].children, 1, [Store Sales]) Trzeba zamienić zbiór w element Dwie metody: TopCount([Bread].children, 1, [Store Sales]).Item(0) Sum( TopCount([Bread].children, 1, [Store Sales]) )
Przykładowa statystyka Podaj o ile gorsza jest sprzedaż danego produktu względem najlepszego w jego kategorii with member Measures.[Stat1] as '[Store Sales] - ( TopCount([Bread].children, 1, [Store Sales]).item(0), [Store Sales] )' {[Store Sales], [Unit Sales], [Stat1]} on columns, Descendants([Product].[Bread]) d [B d]) on rows
Najlepszy potomek Dla każdej kategorii produktu podaj sprzedaż jego najlepszego i najgorszego produktu with member Measures.[Worst] as ' Min(Descendants([Product].CurrentMember,[product].[product name]), [Store Sales])' {[Store Sales],[Worst]} on columns, [Product].members on rows
Najlepszy i najgorszy potomek Dla każdej kategorii produktu podaj sprzedaż jego najlepszego i najgorszego produktu with member Measures.[Worst] as ' Min(Descendants([Product].currentmember,[product].[product name]), [Store Sales])' member Measures.[Best] as ' Max(Descendants([Product].currentmember,[product].[product currentmember [product] [product name]), [Store Sales])' {[Store Sales], [Worst], [Best]} on columns,...
Funkcje obsługi czasu PeriodsToDate([ <level> ]) Domyślnie zwraca elementy od firstsibling do aktualnego Po podaniu poziomu resetuje się na podanym poziomie Przykład: sum(periodstodate(),[store Sales]) Odpowiednik PeriodsToDate([Year]) to funkcja YTD()
Funkcje obsługi czasu LastPeriods( <N> ) Zwraca zbiór elementów od aktualnego do N w tyłł Przykład: dla kwietnia 1998 LastPeriods(2) zwróci zbiór: lut1998, mar1998, kwi1998 Stosowanie do funkcji agregujących Avg(LastPeriods(3),[Unit Sales]) średnia z ostatnich trzech okresów
Funkcja Rank Rank( <member>, <set>) Zwraca pozycję elementu w zbiorze with member Measures.[Info] as 'Rank([Customer].CurrentMember, [Customer].parent.children)' {[Store Sales], [Info]} on columns, [City].members on rows
Funkcja Rank Funkcja działa niezależnie od sortowania with member Measures.[Info] as 'Rank([Customer] Rank([Customer].CurrentMember, CurrentMember [Customer].parent.children)' {[Store Sales], [Info]} on columns, Order([City].members,[Store [St Sales]) on rows
Przechowywanie danych przygotował: pawel@kasprowski.pl
Schematy przechowywania ROLAP (Relational OLAP) wszystkie dane przechowywane w bazie MOLAP (Multidimensional OLAP) dane przechowywane w specjalnej strukturze wielowymiarowej (Analysis Services) HOLAP (Hybrid OLAP) agregacje g w przechowywane w strukturze wielowymiarowej dane przechowane w bazie relacyjnej
ROLAP Zapytania są wolniejsze Brak redundancji powielanie tych samych danych Lepsza skalowalność Zwykle do bardzo dużych baz danych
MOLAP Kopia wszystkich danych w strukturze wielowymiarowej i Najszybsze zapytania Efektywna kompresja Dla mocno obciążonych hurtowni Baza relacyjna jest używana tylko podczas ładowania danych
HOLAP Kompromis pomiędzy szybkością a wielkością ś i bazy Agregacje w strukturze wielowymiarowej Szczegółowe dane w bazie relacyjnej Automatyczna aktualizacja agregacji w przypadku zmian w bazie relacyjnej Na czas aktualizacji działa jak ROLAP
Storage w SQL Server Real Time ROLAP Real Time HOLAP Low Latency MOLAP automatyczna aktualizacja (max. 30 min) przełącza się na ROLAP na czas aktualizacji Medium Latency MOLAP automatyczna aktualizacja (max. 4 h) przełącza ą się ę na ROLAP na czas aktualizacji Automatic MOLAP automatyczna aktualizacja zapytania zawieszają się na czas aktualizacji
Storage w SQL Server Scheduled MOLAP Brak bezpośredniej aktualizacji Automatyczna aktualizacja co 24h MOLAP Brak bezpośredniej aktualizacji Aktualizacja ręczna
Zmiana parametrów przygotował: pawel@kasprowski.pl
Opcje aktualizacji