Dienstag, 5. Mai 2015

DAX Queries über Datenschnitte steuern (semi-automatisch)

Hallo zusammen,

ich beschäftige mich seit einer Weile mit dem Thema DAX Queries und was mir bei diesem tollen Werkzeug wirklich fehlt, ist die Möglichkeit diese über Datenschnitte zu steuern. 

Nehmt folgendes Beispiel:



Abbildung 1 zeigt das zugrunde liegende Datenmodell. Es handelt sich hierbei um eine Faktentabelle, die Kostenstellen pro Tag Kosten zuweist. Als Dimensionstabellen dienen zwei Tabellen, die ihrerseits die Kostenstellen und das Datum konkretisieren.

Abbildung 1: Das Datenmodell
Die einfachste Form eines DAX Queries ist die simple Wiedergabe einer kompletten, im Datenmodell befindlichen Tabelle. Nehmen wir an die Tabelle FactKosten möge aus einem speziellen Grund in einem Excel-Tabellenblatt wiedergegeben werden (natürlich unter der Voraussetzung, dass diese die Zeilengrenze von Excel nicht übersteigt). 

Hierfür muss zunächst eine Verbindung zwischen einer Tabelle und dem Datenmodell hergestellt werden. Wie dies zu bewerkstelligen ist, zeigt Abbildung 2.

Abbildung 2: Verbindungsaufbau mit dem Datenmodell

Nachdem man den Dialog in Abbildung 3 mit OK bestätigt hat, sieht das Ergebnis aus wie in Abbildung 4. 

Abbildung 3: Den Datenimport definieren

Bei dieser Tabelle handelt es sich um das exakte Abbild der Tabelle FactKosten aus dem Datenmodell. 

Abbildung 4: Tabelle mit Verbindung zum Datenmodell
So weit, so gut, aber hierbei handelt es sich noch nicht um ein DAX Query, sondern lediglich um eine vorbereitende Maßnahme. Auf welchem relativ unkomfortablem Weg das DAX Query denn nun endlich definiert werden kann, zeige ich Euch mit Abbildung 5. Mit einem Rechtsklick auf die Tabelle kann der Dialog zum Bearbeiten des DAX Queries geöffnet werden. 

Abbildung 5: So geht's zum Definieren des DAX Queries

Dort angelangt findet man den Punkt 1 aus Abbildung 6 vor. Hier wird definiert, dass die Tabelle FactKosten aus dem Datenmodell komplett zurückgegeben werden soll. Um von hier zu einem DAX Query zu gelangen, welches in einem weiteren Schritt zum Beispiel in der Lage wäre die zurück zu liefernde Tabelle zu filtern und beliebig zu modifizieren, bedarf es zunächst der Umstellung des Befehlstyps von Tabelle auf DAX. Dadurch weiß Excel, dass die Tabelle, wie sie in Abbildung 4 zu sehen ist, nicht die Kopie einer Tabelle aus dem Datenmodell, sondern das Ergebnis eines DAX Queries, also einer DAX Abfrage, sein soll. 

Anschließend ist es notwendig den DAX-Befehl EVALUATE vor FactKosten zu setzen, weil auf diese Weise alle DAX Queries eingeleitet werden. Dies sendet an das Datenmodell quasi die Nachricht, dass alle kommenden Befehle in Form einer zweidimensionalen Tabelle zurückgeliefert werden sollen und nicht, wie sonst üblich, über DAX ein Measure, oder eine kalkulierte Spalte definiert werden soll.

Abbildung 6: Von der Tabelle zum DAX Query

Was haben wir bis hierher geschafft?! Das Ergebnis scheint immer noch das gleiche zu sein. Die Tabelle ist immer noch dieselbe, wie die aus dem Datenmodell. Richtig! Die Tabelle ist immer noch die gleiche, aber wir wären jetzt in der Lage ein DAX-Statement zu schreiben, dass mehr tut, als nur EVALUATE FactKosten. Wir könnten beispielsweise nach dem Monat 3 (also März) filtern. Das würde dann wie in Abbildung 7 aussehen:

Abbildung 7: Filtern in DAX-Queries

Hier soll es jedoch darum gehen, wie wir diese eben erstellte Tabelle über einen Datenschnitt steuern können. Hierfür gibt es in Excel 2013 keinen Weg, der an Einfallsreichtum und VBA-Programmierung vorbeiführt, weil Microsoft in der gegenwärtigen Version nicht vorgesehen hat, Ergebnisse von DAX Queries über Datenschnitte steuern zu lassen.

Dennoch: 
Unsere italienischen Freunde haben in ihrem Buch Microsoft Excel 2013 - Building Data Models with PowerPivot auf Seite 454 gezeigt, dass man die DAX-Definition hinter unserer Tabelle auch über VBA festlegen kann, indem man auf die OLEDB-Connection des TableObjects per VBA zugreift. Ich habe dies bereits einmal selbst programmiert und ich finde es super, dass es überhaupt funktioniert, aber es ist mit Sicherheit nicht komfortabel. Dann kam mir vor kurzem eine Idee, wie man diesen Prozess vereinfachen kann. 

Zunächst füge ich einen Datenschnitt ein, der aus der DimDate-Tabelle den Kalendermonat abbildet. Dieser Datenschnitt wird uns nachher ermöglichen das DAX Query zu steuern.

Abbildung 8: Einfügen eines Datenschnittes mit Verbindung zum Datenmodell

Im Anschluss definiere ich im Datenmodell ein Measure, das es mir ermöglichen wird den größten, im Datenschnitt selektierten Monat auszulesen.

Abbildung 9: Measure zur Ermittlung des maximal selektierten Monats

In Kombination mit einer entsprechenden Cubeformel in Excel, kann ich den maximalen Wert des Datenschnittes in einer Zelle auslesen (siehe Abbildung 10).

Abbildung 10: Den Datenschnitt über Cubefunktionen auslesen

Jetzt, wo wir den ausgelesenen Wert in einer Zelle haben, verknüpfen wir diese Zelle ins Datenmodell (siehe Abbildung 11).

Abbildung 11: Den Kalendermonat ins Datenmodell hinzufügen


Das Resultat ist eine zusätzliche, nicht verknüpfte Tabelle mit nur einer Spalte und exakt einem Wert - unserem Kalendermonat (Abbildung 12). 

Abbildung 12: Der Kalendermonat als unverknüpfte Tabelle
An dieser Stelle angekommen, haben wir also eine Parametertabelle, die den Kalendermonat enthält, den wir über den Datenschnitt selektiert haben. Jetzt müssen wir den selektierten Parameter nur noch dem DAX Query übergeben. Dies geschieht wie folgt. 

Marco hat vor wenigen Tagen seine Serie zu From SQL to DAX erweitert und einen Abschnitt über "Joining tables without relationships in DAX" geschrieben (hier geht's zu Marcos Artikel). In diesem beschreibt Marco, wie man bei nicht verknüpften Tabellen einen Inner Join mit DAX erstellt. Für all jene, denen SQL fremd ist, geht's hier zu Wikipedia. 

Wir gehen also wieder zu unserer Tabelle in Excel, welche das DAX Query als Datenbasis beinhaltet, führen einen Rechtsklick mit unserer Maus durch und gehen, wie in Abbildung 5, zurück zur Definition des DAX Queries. Jetzt schreiben wir es wie folgt um (Abbildung 13):

Abbildung 13: Die finale Definition des DAX Queries mit "Bezug zum Datenschnitt"

Dieses DAX-Query bedarf sicherlich einer Erläuterung:
Die Funktion CROSSJOIN gibt das Kreuzprodukt der beiden Tabellen FactKosten und unserer Parametertabelle Tabelle7 zurück. Da das Kreuzprodukt alle möglichen Kombinationen beider Tabellen zurückliefert, wir aber nur jene haben wollen, bei denen der Monat in der Tabelle FactKosten dem Monat entspricht, der sich gerade in der Parametertabelle Tabelle7 befindet (ausgewählt durch unseren Datenschnitt), wird der CROSSJOIN in eine FILTER-Funktion gebettet, die durch die Bedingung MONTH( FactKosten[Datum] ) = Tabelle7[Spalte1] nur jene Datensätze in der FactKosten-Tabelle übrig läßt, bei denen der Monat mit dem selektierten Monat übereinstimmt. 

Wow, das war ein Ritt. Und ich danke jedem, der bis hier an Bord geblieben ist. Aber mal ehrlich... ist das nicht cool??? 

Bleibt nur noch eines: Jetzt müssen wir der Tabelle in Excel nur noch beibringen auf die Aktualisierung durch den Datenschnitt zu reagieren. Dies geschieht leider nicht automatisch, auch nicht, wenn die Optionen für die Aktualisierung der verknüpfte Tabelle7 in Power Pivot auf automatisch gestellt sind... 

Das ganze ist sicherlich auch über VBA zu lösen, sei es über das Drücken eines zusätzlichen Knöpfes, oder gar über Ereignisprozeduren, die auf die Änderung des Wertes in Zelle B12 gucken. Ich empfehle für die Aktualisierung hier jedoch einen simplen Rechtsklick auf die Tabelle, wie in Abbildung 14 ersichtlich. 

Abbildung 14: Die semi-automatische Aktualisierung der Excel-Tabelle
Danke für Eure Geduld beim Lesen und bis bald,
Lars


Und hier geht's zur Beispieldatei (Excel 2013)