Design einer multidimensionalen SSAS Datenbank mit Biml

Design einer multidimensionalen SSAS Datenbank mit Biml

This blog post has also been published in English.

Sie wissen vielleicht bereits, dass sich mit Biml nicht nur SSIS Pakete erstellen lassen, sondern zum Beispiel auch Analysis Services Datenbanken.

Dieser Blog-Post geht davon aus, dass Sie sich mit der Grundstruktur von Biml bereits beschäftigt haben und bezieht sich primär auf die zusätzlich benötigten Attribute und Tags. Falls Sie das Gefühl haben, dass Sie der Zeit damit einen Schritt voraus sind: Kein Problem. Sehen Sie sich einfach nochmals die Einsteiger Beiträge (am Ende dieses Beitrags) an. Der Beitrag setzt auch entsprechende Grundkenntnisse in multidimensionalen SSAS Datenbanken voraus.

Dieser Beispielcode erzeugt einen einzigen Cube mit einer Measuregroup und einer Partition. Er wird drei Dimensionen beinhalten, davon eine Role Playing Dimension, um ein Grundverständnis zu schaffen. Er basiert auf dem AdventureWorks 2014 DW, welches somit als Voraussetzung vorab einzuspielen ist.

Sobald diese Datenbank in einer SQL Instanz bereitsteht können wir mit einer einfachen Biml Datei zur Definition der Verbindungen loslegen:

<Biml xmlns="http://schemas.varigence.com/biml.xsd">
    <Connections>
        <AnalysisServicesConnection Name="AW_Cube" Server="localhost" ConnectionString="provider=MSOLAP;Server=localhost;Database=Analysis;Integrated Security=SSPI" />
		<OleDbConnection Name="AW_DWH" ConnectionString="provider=SQLNCLI11;data source=.;integrated security=SSPI;Initial Catalog=AdventureWorksDW2014" />
    </Connections>
	<Databases>
        <Database Name="AW_DW" ConnectionName="AW_DWH" />
    </Databases>
	<Schemas>
        <Schema Name="dbo" DatabaseName="AW_DW" />
    </Schemas>
</Biml>

Außer vielleicht der SSAS Verbindung sollte hier nichts Neues enthalten sein.
Was auch anzumerken ist: Der gesamte Beispielcode besteht aus statischen Biml Dateien – es kommt kein BimlScript zum Einsatz. Natürlich könnte BimlScript hier einiges beisteuern, aber verbessern kann man es hinterher immer 🙂

Fangen wir mit einer sehr einfachen Dimension an: Währung. Dies bewerkstelligen wir, indem wir der Tabelle in Biml einige zusätzliche Informationen anhängen.

Nehmen wir an, dass die Währungs-/Currency-Tabelle aktuell wie folgt aussieht:

<Biml xmlns="http://schemas.varigence.com/biml.xsd">
    <Tables>
        <Table Name="DimCurrency" SchemaName="AW_DW.dbo" FriendlyName="Source Currency">
            <Columns>
                <Column Name="CurrencyKey" />
                <Column Name="CurrencyName" DataType="String" Length="50" />
            </Columns>
        </Table>
    </Tables>
</Biml>

Um hieraus eine OLAP Dimension zu erstellen benötigen wir einen Primärschlüssel sowie einige weitere Analysis Metadaten:

<Biml xmlns="http://schemas.varigence.com/biml.xsd">
    <Tables>
        <Table Name="DimCurrency" SchemaName="AW_DW.dbo" FriendlyName="Source Currency">
            <Columns>
                <Column Name="CurrencyKey" />
                <Column Name="CurrencyName" DataType="String" Length="50" />
            </Columns>
            <Keys>
                <Identity Name="PK_DimCurrency_CurrencyKey">
                    <Columns>
                        <Column ColumnName="CurrencyKey" />
                    </Columns>
                </Identity>
            </Keys>
            <AnalysisMetadata>
                <Dimension Name="Currency" DimensionType="Currency">
                    <Attributes>
                        <Attribute Name="Currency" Usage="Key" AttributeType="CurrencyIsoCode" OrderBy="Name">
                            <KeyColumns>
                                <KeyColumn ColumnName="CurrencyKey" />
                            </KeyColumns>
                            <NameColumn ColumnName="CurrencyName" />
                        </Attribute>
                    </Attributes>
                </Dimension>
            </AnalysisMetadata>
        </Table>
    </Tables>
</Biml>

Sie sehen zwei neue Tags: Keys und AnalysisMetadata.

Der Keys-Tag definiert den Primärschlüssel, welchen wir für die Verbindung der Fakten- zur Dimensions-Tabelle benötigen.
Der AnalysisMetadata-Tag besteht in diesem Fall aus den Dimensions-Metadaten, welche eine Collection von Attributen (hier nur eines) beinhaltet, welche wir anhand von Namen, Schlüssel- und Namensspalten sowie optionalen Attributen wie Sortierreihenfolgen definieren.

Die meisten Dimensionen bestehen jedoch aus mehreren Attributen und mindestens einer Hierarchie:

 <Biml xmlns="http://schemas.varigence.com/biml.xsd">
	<Tables>
		<Table Name="DimSalesTerritory" SchemaName="AW_DW.dbo" FriendlyName="Sales Territory" >
			<Columns>
				<Column Name="SalesTerritoryKey" />
				<Column Name="SalesTerritoryRegion" DataType="String" Length="50" />
				<Column Name="SalesTerritoryCountry" DataType="String" Length="50" />
				<Column Name="SalesTerritoryGroup" DataType="String" Length="50" IsNullable="true" />
			</Columns>
			<Keys>
				<Identity Name="PK_DimSalesTerritory_SalesTerritoryKey">
					<Columns>
						<Column ColumnName="SalesTerritoryKey" />
					</Columns>
				</Identity>
			</Keys>
			<AnalysisMetadata>
				<Dimension Name="Sales Territory"  AttributeAllMemberName="All Sales Territories">
					<Attributes>
						<Attribute Name="Sales Territory Region" OrderBy="Name" Usage="Key"> 
							<KeyColumns>
								<KeyColumn ColumnName="SalesTerritoryKey" />
							</KeyColumns>
							<NameColumn ColumnName="SalesTerritoryRegion" />
						</Attribute>
						<Attribute Name="Sales Territory Group" OrderBy="Name">
							<KeyColumns>
								<KeyColumn ColumnName="SalesTerritoryGroup" />
							</KeyColumns>
							<NameColumn ColumnName="SalesTerritoryGroup" />
						</Attribute>
						<Attribute Name="Sales Territory Country" OrderBy="Name">
							<KeyColumns>
								<KeyColumn ColumnName="SalesTerritoryCountry" />
							</KeyColumns>
							<NameColumn ColumnName="SalesTerritoryCountry" />
						</Attribute>
					</Attributes>
					<AttributeHierarchies>
						<Hierarchy Name="Sales Territory">
							<Levels>
								<Level Name="Group" AttributeName="Sales Territory Group" />
								<Level Name="Country" AttributeName="Sales Territory Country" />
								<Level Name="Region" AttributeName="Sales Territory Region" />
							</Levels>
						</Hierarchy>
					</AttributeHierarchies>
					<Relationships>
						<Relationship Name="Sales Territory Country" ParentAttributeName="Sales Territory Region" ChildAttributeName="Sales Territory Country" />
						<Relationship Name="Sales Territory Group" ParentAttributeName="Sales Territory Country" ChildAttributeName="Sales Territory Group" />
					</Relationships>
				</Dimension>
			</AnalysisMetadata>
		</Table>
	</Tables>
</Biml>

Die hier vorliegende „Sales Territory“-Dimension besteht zum Beispiel aus 3 Attributen. Um diese in den richtigen Kontext zu bringen, fügen wir eine Hierarchie mittels dem AttributeHierarchies-Tag hinzu und verbinden die Attribute über Relationships. Der Hierarchie geben wir lediglich einen Namen und fügen die einzelnen Level hinzu, indem wir auf die Attribute verweisen.
Für die Relationships (Beziehungen) fügen wir jedes Nicht-Schlüssel-Attribut einen Relationship-Tag hinzu, welcher wiederum aus einem Namen sowie aus einem Parent und einem Child Attribut besteht.
Vorsicht: Die Reihenfolge ist hier umgekehrt zu den Hierarchien. Wenn Ihre Hierarchie zum Beispiel Land-Stadt-Kunde ist, so wäre der Kunde das erste Parent-Attribut und die Stadt das erste Child-Attribut. Die Hierarchie arbeitet also Ihren Weg von der höchsten Aggregationsstufe nach unten – die Beziehungen arbeiten sich von der untersten Ebene nach oben.

Zusätzlich definieren wir noch eine Zeitdimension:

 <Biml xmlns="http://schemas.varigence.com/biml.xsd">
    <Tables>
        <Table Name="DimDate" SchemaName="AW_DW.dbo" FriendlyName="Date">
            <Columns>
                <Column Name="DateKey" />
                <Column Name="FullDateAlternateKey" DataType="Date" />
                <Column Name="CalendarYear" DataType="Int16" />
                <Column Name="EnglishMonthName" DataType="String" Length="10" />
                <Column Name="MonthNumberOfYear" DataType="Byte" />
            </Columns>
            <Keys>
                <PrimaryKey Name="PK_DimDate_DateKey">
                    <Columns>
                        <Column ColumnName="DateKey" />
                    </Columns>
                </PrimaryKey>
            </Keys>
            <AnalysisMetadata>
                <Dimension Name="Date" DimensionType="Time">
                    <Attributes>
                        <Attribute Usage="Key" Name="Date" AttributeType="Date">
                            <KeyColumns>
                                <KeyColumn ColumnName="DateKey" />
                            </KeyColumns>
                            <NameColumn ColumnName="FullDateAlternateKey" />
                        </Attribute>
                        <Attribute Name="Month Name" AttributeType="Months">
                            <KeyColumns>
                                <KeyColumn ColumnName="CalendarYear" />
                                <KeyColumn ColumnName="MonthNumberOfYear" />
                            </KeyColumns>
                            <NameColumn ColumnName="EnglishMonthName" />
                        </Attribute>
                        <Attribute Name="Calendar Year" AttributeType="Years">
                            <KeyColumns>
                                <KeyColumn ColumnName="CalendarYear" />
                            </KeyColumns>
                            <NameColumn ColumnName="CalendarYear" />
                        </Attribute>
                    </Attributes>
                    <Relationships>
                        <Relationship Name="Month Name" ParentAttributeName="Date" ChildAttributeName="Month Name" />
                        <Relationship Name="Calendar Year" ParentAttributeName="Month Name" ChildAttributeName="Calendar Year" />
                    </Relationships>
                    <AttributeHierarchies>
                        <Hierarchy Name="Calendar">
                            <Levels>
                                <Level Name="Year" AttributeName="Calendar Year" />
                                <Level Name="Month" AttributeName="Month Name" />
                                <Level Name="Date" AttributeName="Date" />
                            </Levels>
                        </Hierarchy>
                    </AttributeHierarchies>
                </Dimension>
            </AnalysisMetadata>
        </Table>
    </Tables>
</Biml>

Neben der DimensionType- und AttributeTypes-Eigenschaften, welche wir zum Beispiel für eine Year-To-Date Berechnung benötigen, ist hier nichts neu im Vergleich zu den vorhergehenden Dimensionen.

Übrigens: Am Beispiel „Month“, welches mehrere Schlüsselspalten hat, sieht man schön, weswegen die KeyColumns eine Collection sind und kein einzelnes Attribut.

Jetzt, wo wir alle Dimensionen definiert haben, folgt die Measuregroup:

<Biml xmlns="http://schemas.varigence.com/biml.xsd">
    <Tables>
        <Table Name="FactInternetSales" SchemaName="AW_DW.dbo">
            <Columns>
                <TableReference Name="OrderDateKey" TableName="AW_DW.dbo.DimDate" />
                <TableReference Name="ShipDateKey" TableName="AW_DW.dbo.DimDate" />
                <TableReference Name="CurrencyKey" TableName="AW_DW.dbo.DimCurrency" />
                <TableReference Name="SalesTerritoryKey"  TableName="AW_DW.dbo.DimSalesTerritory" />
                <Column Name="OrderQuantity" DataType="Int16" />
                <Column Name="SalesAmount" DataType="Currency" />
            </Columns>
            <AnalysisMetadata>
                <MeasureGroup Name="Internet Sales">
                    <Measures>
                        <Measure AggregateColumnName="OrderQuantity" Name="Order Quantity" />
                        <Measure AggregateColumnName="SalesAmount" Name="Sales Amount" />
                    </Measures>
                </MeasureGroup>
            </AnalysisMetadata>
        </Table>
    </Tables>
</Biml>

Im Vergleich zu „normalen“ Faktentabellen, die Sie vielleicht aus bestehenden Biml-Projekten kennen, gibt es 2 Unterschiede:
Es gibt auch hier wieder einen AnalysisMetadata-Tag, welche in diesem Fall statt einer Dimension eine MeasureGroup beschreibt. Dieser Bereich ist relativ selbsterklärend, er beschreibt lediglich die (numerischen) Spalten, welche als Measure in der Measuregroup bereitstehen sollen.
Der andere Teil ist vielleicht etwas neuer: Einige Spalten sind nicht als Column sondern als TableReference, also ein Verweis auf eine entsprechende Dimensionstabelle, definiert.

Nun fehlt nur noch der Cube. Dieser ist im Wesentlichen eine Zusammenführung von zuvor definierten Biml-Objekten:

<Biml xmlns="http://schemas.varigence.com/biml.xsd">
    <Cubes>
        <Cube Name="MyCube" ConnectionName="AW_Cube">
            <CubeMeasureGroups>
                <CubeMeasureGroup Name="Internet Sales" FactName="AW_DW.dbo.FactInternetSales.Internet Sales">
                    <CubeDimensionBindings>
                        <CubeDimensionBinding CubeDimensionName="Order Date" FactColumnName="AW_DW.dbo.FactInternetSales.OrderDateKey" />
                        <CubeDimensionBinding CubeDimensionName="Ship Date" FactColumnName="AW_DW.dbo.FactInternetSales.ShipDateKey" />
                        <CubeDimensionBinding CubeDimensionName="Currency" FactColumnName="AW_DW.dbo.FactInternetSales.CurrencyKey" />
                        <CubeDimensionBinding CubeDimensionName="Sales Territory" FactColumnName="AW_DW.dbo.FactInternetSales.SalesTerritoryKey" />
                    </CubeDimensionBindings>
                    <Partitions>
                        <Partition Name="Internet Sales Partition">
                            <DsvTableSource ConnectionName="AW_DWH" TableName="AW_DW.dbo.FactInternetSales" />
                        </Partition>
                    </Partitions>
                </CubeMeasureGroup>
            </CubeMeasureGroups>
            <CubeDimensions>
                <CubeDimension Name="Currency" DimensionName="AW_DW.dbo.DimCurrency.Currency" />
                <CubeDimension Name="Order Date" DimensionName="AW_DW.dbo.DimDate.Date" />
                <CubeDimension Name="Ship Date" DimensionName="AW_DW.dbo.DimDate.Date" />
                <CubeDimension Name="Sales Territory" DimensionName="AW_DW.dbo.DimSalesTerritory.Sales Territory" />
            </CubeDimensions>
        </Cube>
    </Cubes>
</Biml>

Nachdem wir die CubeDimensions (welche auf unsere zuvor definierten Dimensionen verweisen), die CubeMeasureGroups (welche wiederum auf unsere zuvor definierten Measuregroups verweisen) sowie die CubeDimensionBindings (hier definieren wir auch eine Role Playing Dimension indem wir zwei Mal auf die „Date“ Dimension verweisen) hinterlegt haben, liegt alles für unser SSAS Projekt benötigte vor. Wenn Sie nun die Biml-Solution kompilieren, finden Sie das SSAS Projekt im Output\SSAS Ordner.

Haben Sie hierzu Fragen oder Anmerkungen? Wir freuen uns auf Ihren Input unter biml@solisyon.de!

Weitere Informationen zu Biml, einschließlich Terminen und Blog Beiträgen finden Sie auch auf unserer Biml Seite.

Viel Spaß beim Biml’n!

Facebook