Archiv der Kategorie: Visual Studio

mehrere DAX-Statements als Quelle in Cubes

Motivation

In der Regel lesen Cubes (tabulare Modelle) Daten aus einem DWH, also meist relationalen Datenbanken. Es kann aber sinnvoll sein, das Ergebnis eines Cubes als Quelle für einen anderen Cube zu verwenden. In einem Kundenprojekt benötigten wir das, weil die Preisberechnung in einem Cube implementiert war und in einem anderen Cube verwendet werden sollte.

Dann hat man also ein oder mehrere DAX-Statements, die man in den Cube laden will. Während die Verwendung eines DAX-Statements gradlinig funktioniert, ist es schwer, mehrere DAX-Statements (mit dem gleichen Cube als Quelle) zu verwenden.

Dieser Post zeigt, wie es funktioniert.

Problem

Nehmen wir für unser Beispiel an, dass wir zwei DAX-Abfragen von einem bestehenden Cube in einen neuen Cube importieren wollen. Um ein einfaches Beispiel zu haben, verwenden wir als Abfrage einfach:

evaluate {1}

Dies liefert eine Tabelle mit einer Spalte („[Value]“) und einer Zeile, die den Wert 1 hat.

Um diese Abfrage in den neuen Cube zu laden, gehen wir ganz normal im Visual Studio vor – genauso wie man es in Power BI auch machen würde:

Wir definieren eine neue Datenquelle vom Typ „SQL Server Analysis Services-Datenbank“:

Eingabe eins DAX-Statements bei einer neu erstellten Datenquelle vom Typ SSAS

Um diese Daten als Tabelle im Cube zu haben, muss man auf der Datenquelle rechte Maustaste > „Import New Tables“anklicken. Das Power-Query-Fenster öffnet sich:

Power-Query-Editor für die DAX-Abfrage

Über „Import“ wird diese Abfrage automatisch ausgeführt und man erhält eine neue Tabelle mit Namen „Table“, die nur eine Spalte „[Value]“ hat und dort eine Zeile mit 1:

Tabelle mit Ergebnis des DAX-Statements

Versucht man aber nun analog eine zweite Abfrage (mit evaluate {2}) auszuführen, scheint es zunächst zu funktionieren. Allerdings wird keine zweite Datenquelle angelegt, sondern die bestehende verändert, so dass bei einem Refresh die oben gezeigte Tabelle eine 2 statt einer 1 zeigt.

Es ist nicht möglich, zwei unterschiedliche DAX-Abfragen auf dieselbe Quelle abzufeuern

Man muss es also anders probieren.

Lösungsidee

Bei meinem Blogeintrag zur Datumsdimension in SSAS hatte ich bereits die M-Syntax Value.NativeQuery verwendet. Dies werden wir hier mit der Datenquelle SSAS verwenden.

Dazu gehen wir wie folgt vor:

Zunächst löschen wir die Datenquelle mit dem DAX-Statement und erstellen eine neue, allerdings ohne Eingabe eines DAX-Statements.

Quelle vom Typ SSAS

Im zweiten Fenster geben wir nur die Connection an:

Verbindung zu einer SSAS Datenbank – ohne DAX-Statement

Um auszuprobieren, ob sie funktioniert, gehen wir mit der rechten Maustaste auf diese Verbindung und klicken auf „Import new tables“:

Auswahl einer kleinen (!) Tabelle

Wir sollten darauf achten, dass wir eine kleine Tabelle auswählen. Dann klicken wir auf „Load“.

Damit wird eine neue Tabelle namens „Model“ mit dieser Struktur angelegt. Das M hinter der Abfrage finden wir in den Eigenschaften bei Source Data:

Eigenschaften der neuen Tabelle –> Source Data, um M zu sehen

Das M-Statement sieht so aus:

let
    Source = #"Name der Datenquelle",
    Model1 = Source{[Id="Model"]}[Data],
    Model2 = Model1{[Id="Model"]}[Data],
    #"Added Items" = Cube.Transform(Model2,
        {
            {Cube.AddAndExpandDimensionColumn, "[DimChannel]", {"[DimChannel].[ChannelDescription].[ChannelDescription]", "[DimChannel].[ChannelKey].[ChannelKey]"}, {"DimChannel.ChannelDescription", "DimChannel.ChannelKey"}}
        })
in
    #"Added Items"

Wenn wir das Statement nun so abändern, haben wir die Problemstellung gelöst:

let
    Source = #"Name der Datenquelle",
    Tabelle = Value.NativeQuery(Source, "evaluate {1}")
in
    Tabelle

Nach dem Löschen der alten zwei Spalten und einem Refresh, sieht das Ergebnis wie gewünscht aus:

Ergebnis der DAX-Abfrage als Tabelle

Um nun zu zeigen, dass die zweite Abfrage auch funktioniert, erstellen wir einfach eine zweite Partition mit der gleichen M-Abfrage, wobei wir nur 1 durch 2 ersetzen:

2 Partitionen

Nach einem Refresh auf die Tabelle haben wir das Ergebnis:

fertig: Beide DAX-Statements wurden ausgeführt

Damit sind wir fertig!

Ich habe absichtlich das Beispiel der Partitionen verwendet, da dies ein klassisches Anwendungsszenario ist, bei dem mehrere Abfragen auf die gleiche Quelle abgefeuert werden müssen.

Optimierungen

Der Nachteil an dieser Implementierung ist, dass das DAX-Statement immer komplett geschrieben werden muss.

Wenn man sich aber nun vorstellt, dass es ein kompliziertes DAX-Statement ist, das sich je Partition nur um eine Zahl unterscheidet (in unserem Beispiel eine Zahl von 0 bis 99), dann möchte man das DAX-Statement nicht 100x schreiben – weil das natürlich sehr schlecht zu warten ist.

Deswegen sehen bei uns die M-Befehle so aus:

let
    Source = #"Name der Datenquelle",
	P = Value.NativeQuery(
        Source, Text.Combine({Vorne, "1", Hinten}, "")
	)
in
    P

Wir sehen, dass wir zwei Variablen (Vorne und Hinten) verwenden. In der gezeigten Partition wird dann die Zahl 1 zwischen Vorne und Hinten eingetragen.

In unserem Beispiel aus dem Blog wäre

Vorne = "evaluate {"

und

Hinten = "}"

Jetzt ist nur noch die Frage, wo wir diese Variablen hinterlegen können. Das sind die Expressions:

Expressions

Um diese zu erstellen, klicken wir mit der rechten Maustaste auf „Expressions“ und dann auf „Edit Expressions“:

Dann legen wir eine leere Query an:

eine leere Quelle im Power Query Editor anlegen

Die Query können wir umbenennen über die Name-Property. Den Wert der Variablen tippen wir oben einfach ein:

Beispiel der Variable „Vorne“

In unserem echten Anwendungsfall haben wir den Text (hier evaluate { ) nicht einfach eingetragen, sondern mit = zugewiesen, also = "evaluate {" geschrieben. Dann muss man Anführungszeichen, die in dem DAX-Statement vorkommen, verdoppeln, da der Interpreter sonst denkt, der String wäre zu Ende.

Deployment eines Cubes als Power BI Premium Dataset

Seit über einem Jahr gibt es nun die Möglichkeit, Cubes, die man – wie bisher auch – in Visual Studio erstellt, nun direkt nach Power BI in einen Arbeitsbereich zu deployen.

Dies hat einige Vorteile:

  • Man muss keine eigenen Analysis Services Dienste mehr vorhalten (weder on prem noch Azure Analysis Services)
  • Das spart ggf. Lizenzen, Server-Kapazitäten und verringert die Komplexität einer Lösung
  • Man kann dennoch auf die Datasets von außerhalb zugreifen (z.B. Excel Pivot, Reporting Services – generell mit jedem Tool, das Zugriffe auf Cubes unterstützt)

Als wir zum ersten Mal ein solches Cube-Deployment allerdings durchgeführt haben, ging das nicht, ohne über die ein oder andere Klippe zu stolpern. Deshalb beschreibe ich hier, wie man vorgehen muss.

Voraussetzungen

Ausgangssituation: Ich gehe davon aus, dass wir im Visual Studio einen Cube erstellt haben.

Wichtig ist, dass wir den Kompatibilitätsmodus auf 1500 gesetzt haben:

Kompatibilitätsmodus auf 1500 gesetzt

(Man beachte, dass diese Einstellung nur bearbeitet werden kann, wenn das Model.bim im Visual Studio geöffnet ist)

Build und Deployment

Über einen Build erzeugen wie dann ein asdatabase-File:

Build eines Cube-Projekts innerhalb Visual Studio

Das erzeugt im bin-Folder folgende Dateien (das ist noch alles unabhängig von unserem Deployment nach Power BI Premium):

Ordner mit den daraus resultierenden Dateien

In der Regel machen wir es nun so, dass wir in unser Quell-Code-Verwaltungssystem das „Model.asdatabase“ einchecken und von einem Branch in den nächsten (-> Test -> Prod) bewegen – die anderen Dateien werden für den Deployment Wizard nicht benötigt. Hier werden wir gleich einen Fallstrick sehen – aber dazu gleich mehr.

Nun wollen wir das so erzeugte Model.asdatabase-File in einen Power BI Premium-Arbeitsbereich deployen. Dazu benötigen wir als erstes die Adresse, auf die wir es deployen können. Diese finden wir im Arbeitsbereich.

Es ist zu beachten, dass das ein Premium-Feature ist. Der Arbeitsbereich muss deshalb entweder einer Power-BI-Premium-Kapazität zugeordnet sein (erkennbar am Symbol ) oder unter einer Power BI Premium Einzelbenutzerlizenz laufen (erkennbar am Symbol ). Unter Einstellungen findet man dann im Reiter Premium die Adresse für die Verbindung:

Einstellungen des Power BI Premium Arbeitsbereichs

Mit der Arbeitsbereichverbindung kann man sich dann auch im SQL Server Management Studio auf den XMLA-Endpoint verbinden. Das werden wir später noch brauchen.

Nun können wir versuchen, den Cube zu deployen. Dazu starten wir den Analysis Services Deployment Wizard und wählen die Model.asdatabase-Datei aus. Als Server tragen wir die powerbi://-Adresse von oben ein:

Eingabe des Servers und Datasetnamen im Analysis Services Deployment Wizard

Die Eintragungen auf den nächsten Seiten sind nicht relevant, zumal wir ja den Cube zum ersten Mal deployen. In der Regel deploye ich sonst Rollen nicht und übernehme die bisher geltenden Einstellungen. Auf das Default Procesisng verzichten wir jetzt (wir machen das nachher vom SQL Server Management Studio aus).

Witzigerweise erhalten wir die Fehlermeldung „Die Datenbank „BlogDemoCube“ ist nicht vorhanden, oder Sie besitzen keine Zugriffsberechtigung.“ Das ist richtig – aber wir wollen den Cube ja auch erstmalig deployen. Hier der Screen Shot:

Erste Fehlermeldung beim Deployment

Die Lösung für dieses Problem ist, die Datei „Model.deploymentoptions“, die wir vorher gesehen haben, auch in das Verzeichnis zu legen, von wo aus wir die Datei „Model.asdatabase“ deployen.
Wenn wir das aus dem bin-Verzeichnis direkt machen, wäre uns das ganze also gar nicht passiert, da Visual Studio diese Datei dort ja ablegt. Wie gesagt, verwenden wir aber unterschiedliche Branches für unsere Umgebungen und haben dort nur das Model.asdatabase eingecheckt und bisher (onprem Analysis Services oder Azure Analysis Services) konnten wir auch nur mit dem Model.asdatabase deployen.
Die Datei „Model.deploymentoptions“ enthält darüber hinaus auch keine Informationen, die nicht im Wizard abgefragt werden. Das macht das ganze umso komischer.

Die „Model.deploymentoptions“-Datei sieht so aus (es ist also tatsächlich keine wertvolle Information, sondern kann einfach so übernommen werden):

<DeploymentOptions xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
                    xmlns:ddl2="http://schemas.microsoft.com/analysisservices/2003/engine/2" xmlns:ddl2_2="http://schemas.microsoft.com/analysisservices/2003/engine/2/2" 
                    xmlns:ddl100_100="http://schemas.microsoft.com/analysisservices/2008/engine/100/100" xmlns:ddl200="http://schemas.microsoft.com/analysisservices/2010/engine/200" 
                    xmlns:ddl200_200="http://schemas.microsoft.com/analysisservices/2010/engine/200/200">
                    <TransactionalDeployment>false</TransactionalDeployment>
                    <PartitionDeployment>DeployPartitions</PartitionDeployment>
                    <RoleDeployment>DeployRolesRetainMembers</RoleDeployment>
                    <ProcessingOption>Default</ProcessingOption>
                    <ADALCache>None</ADALCache>
                    <OutputScript></OutputScript>
                    <ImpactAnalysisFile></ImpactAnalysisFile>
                    <ConfigurationSettingsDeployment>Deploy</ConfigurationSettingsDeployment>
                    <OptimizationSettingsDeployment>Deploy</OptimizationSettingsDeployment>
                    <WriteBackTableCreation>UseExisting</WriteBackTableCreation>
                </DeploymentOptions>

Durchläuft man nun nochmal den Deployment-Prozess, ändert sich die Fehlermeldung zu „Der Vorgang wird nur für ein Modell unterstützt, dessen Eigenschaft „DefaultPowerBIDataSourceVersion“ in Power BI Premium auf „PowerBI_V3″ festgelegt ist.“:

fehlende Eigenschaft „DefaultPowerBIDataSourceVersion“

Diese Fehlermeldung ist immerhin aussagekräftig. Wir lösen das Problem, indem wir die datei Model.asdatabase in einem Editor (sagen wir Notepad++) bearbeiten. Wir fügen nach „culture“ die geforderte Eigenschaft ein: „defaultPowerBIDataSourceVersion“: „powerBI_V3“,

Vor der Bearbeitung sieht es so aus:

{
  "name": "xxxxxxx",
  "compatibilityLevel": 1500,
  "model": {
    "culture": "de-DE",
    "discourageImplicitMeasures": true,
    "dataSources": [

Und nach der Bearbeitung so:

{
  "name": "xxxxxxx",
  "compatibilityLevel": 1500,
  "model": {
    "culture": "de-DE",
    "defaultPowerBIDataSourceVersion": "powerBI_V3",
    "discourageImplicitMeasures": true,
    "dataSources": [

Nun funktioniert das Deployment 🙂

Der Cube ist im Arbeitsbereich sichtbar:

Cube als Power BI Premium Dataset – sichtbar im Arbeitsbereich

Außerdem sieht man ihn im SQL Server Management Studio:

sichtbar im SQL Server Management Studio

Allerdings sind wir noch nicht fertig, da wir den Cube noch verarbeiten müssen.

Davor möchte ich aber noch ein paar Hinweise geben:

  • Beide „Hacks“ (Model.deploymentoptions und defaultPowerBIDataSourceVersion) sind nur für das erste Deployment relevant. Deployt man eine neue Version des Cubes über ein bestehendes Power BI Premium Dataset, sind beide Veränderungen nicht mehr notwendig.
  • Man könnte defaultPowerBIDataSourceVersion auch im Model.bim eintragen. Allerdings ist das nicht ratsam, da dann das Model.bim nicht mehr in einem Arbeitsbereichserver geöffnet werden kann und man somit nicht mehr im Visual Studio daran weiterarbeiten kann.

Cube verarbeiten

Machen wir also weiter: Wie nach jedem Deployment (auch nach Azure Analysis Services) muss man die Credentials neu eintragen, die für die Verbindung auf die zugrundeliegende Datenbank benutzt werden:

Credentials eintragen

(In unserem Fall verwenden wir SQL Server Authentifizierung auf einen SQL Server in Azure)

Nun könnten wir – wenn es ein Azure Analysis Services-Cube wäre, die Cubeverarbeitung erfolgreich durchführen. Wenn wir aber nun die Verarbeitung starten, kommt eine Fehlermeldung:

Start der Cubeverarbeitung

Die Fehlermeldung lautet: „Failed to save modifications to the server. Error returned: ‚{„error“:{„code“:“DMTS_DatasourceHasNoCredentialError„,“pbi.error“:{„code“:“DMTS_DatasourceHasNoCredentialError“,“details“:[{„code“:“Server“,“detail“:{„type“:1,“value“:“//servername//“}},{„code“:“Database“,“detail“:{„type“:1,“value“:“//datenbankname//“}},{„code“:“ConnectionType“,“detail“:{„type“:0,“value“:“Sql“}}],“exceptionCulprit“:1}}}“. Das ist überraschend, da wir ja die Credentials gesetzt haben. Wir müssen aber noch in der Power BI App im Arbeitsbereich Einstellungen des Datsets machen:

Einstellungen des Datasets aufrufen

Hier sehen wir schon unter „Datenquellen-Anmeldeinformationen“ den Fehler: „Ihre Datenquelle kann nicht aktualisiert werden, da die Anmeldeinformationen ungültig sind. Aktualisieren Sie Ihre Anmeldeinformationen, und versuchen Sie es erneut.“

Fehler unter Datenquellen-Anmeldeinformationen

Die Lösung ist nun nahe liegend: Unter „Anmeldeinformationen bearbeiten“ muss man die gleichen Credentials nochmal eintragen:

Credentials nochmal eintragen

Mit diesen Einstellungen funktioniert jetzt die Cubeverarbeitung:

erfolgreiche Cubeverarbeitung

Damit haben wir die Aufgabe komplett erledigt.

Eine Anmerkung gibt es allerdings noch: Ich habe den Fall mit einer Datenquelle in Azure durchgespielt. Wenn die Datenquelle on premise liegt, ändert sich die letzte Aktion (Einstellungen des Datasets) leicht: In diesem Fall müssen dort die Gatewayeinstellungen korrekt gesetzt werden:

Einstellungen der Gatewayverbindung eines Datasets

Damit man diese Einstellung machen kann, muss der eigene User als Mitglied auf dem gateway eingetragen sein (über Power BI > Einstellungen > Gateways verwalten).