{"id":216,"date":"2026-10-26T20:12:37","date_gmt":"2026-10-26T20:12:37","guid":{"rendered":"https:\/\/nils-becker.org\/?p=216"},"modified":"2026-10-26T20:12:37","modified_gmt":"2026-10-26T20:12:37","slug":"eine-semantik-viele-plattformen-power-bi-nach-snowflake-databricks-und-co","status":"publish","type":"post","link":"https:\/\/nils-becker.org\/?p=216","title":{"rendered":"Eine Semantik, viele Plattformen: Power BI nach Snowflake, Databricks und Co."},"content":{"rendered":"\n<p><br>F\u00fcr viele Unternehmen ist Power BI der Dreh- und Angelpunkt, wenn es um Zahlen, Fakten und Entscheidungen geht. Dort wird die Bedeutung der Daten beschrieben, sie werden transformiert und ihre Zusammenh\u00e4nge modelliert. Diese Informationen bleiben allerdings oft isoliert im Microsoft-Universum. M\u00f6chte man Agenten nutzen, um Insights zu generieren, muss man sie erst aufw\u00e4ndig f\u00fcr den Agenten bereitstellen. H\u00e4ufig wird die Semantik daf\u00fcr von Hand nachgebaut.<\/p>\n\n\n\n<p><br>Ich habe deshalb ein kleines Werkzeug geschrieben: https:\/\/github.com\/s6nlbeck\/tmdl2ossie. Es liest ein Power-BI-Semantikmodell ein und \u00fcberf\u00fchrt es in das herstellerneutrale Format &#8222;Apache Ossie&#8220;. Von dort exportiert es nach Snowflake, Databricks, Cube und Honeydew.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><br>Das Problem: Jeder Semantic Layer spricht seine eigene Sprache<\/h2>\n\n\n\n<p>Semantische Schichten sind gerade \u00fcberall. Snowflake hat semantische Modelle f\u00fcr Cortex Analyst, Databricks hat Metric Views, dazu kommen Cube, Honeydew, dbt und andere. Alle beschreiben im Kern dasselbe: Tabellen, Felder, Beziehungen und Kennzahlen. Und jede Plattform nutzt daf\u00fcr ihr eigenes Format.<\/p>\n\n\n\n<p>Das ist besonders \u00e4rgerlich, weil KI-Agenten genau diese Semantik brauchen, um Halluzinationen zu reduzieren und zuverl\u00e4ssige Antworten zu den Daten zu geben. Ohne sie wissen sie nicht, dass \u201eUmsatz\u201c aus der Verkaufstabelle kommt und \u00fcber die Kunden-ID mit der Region verbunden ist.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Apache Ossie als gemeinsame Sprache<\/h2>\n\n\n\n<p>Apache Ossie ist ein herstellerneutrales YAML-Format f\u00fcr genau diese Beschreibung. Die Idee ist einfach: einmal nach Ossie \u00fcbersetzen oder direkt in Ossie modellieren, dann \u00fcberallhin exportieren. F\u00fcr den Export gibt es bereits eine Reihe fertiger Konverter.<\/p>\n\n\n\n<p>Das Problem dabei: F\u00fcr viele Unternehmen ist Power BI das Semantik-Werkzeug schlechthin. Die Semantik doppelt zu pflegen, ist keine L\u00f6sung. Power BI in den Ruhestand zu schicken, aber leider auch nicht. Die Datenanalysten kennen und sch\u00e4tzen ihr Power BI und ein Umstieg ist mit gro\u00dfem Aufwand verbunden.<\/p>\n\n\n\n<p>Da Power BI selbst kein Ossie exportiert, musste eine L\u00f6sung her, die Power BI weiter nutzt, aber daf\u00fcr sorgt, dass die Semantik nicht aufw\u00e4ndig nachgebaut werden muss. Mein Ziel: Die Semantik wird in Power BI gepflegt und l\u00e4sst sich ohne gro\u00dfen Aufwand auch in Snowflake und Co. nutzen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">So sieht es aus<\/h2>\n\n\n\n<p>Die Oberfl\u00e4che ist eine kleine Web-App mit vier Tabs: Power-BI-Projekt als ZIP hochladen, die \u00fcbersetzte Semantik pr\u00fcfen und bearbeiten, das Datenmodell als Graph ansehen und f\u00fcr die Zielplattform exportieren. Alles l\u00e4uft lokal; es braucht keine Verbindung zu Power BI oder einem Data Warehouse.<\/p>\n\n\n\n<figure class=\"wp-block-gallery has-nested-images columns-default is-cropped wp-block-gallery-1 is-layout-flex wp-block-gallery-is-layout-flex\">\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"507\" data-id=\"220\" src=\"https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/databricks_semantic-1024x507.png\" alt=\"\" class=\"wp-image-220\" srcset=\"https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/databricks_semantic-1024x507.png 1024w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/databricks_semantic-300x148.png 300w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/databricks_semantic-768x380.png 768w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/databricks_semantic-1536x760.png 1536w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/databricks_semantic-2048x1013.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" data-id=\"221\" src=\"https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/editor-1024x512.png\" alt=\"\" class=\"wp-image-221\" srcset=\"https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/editor-1024x512.png 1024w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/editor-300x150.png 300w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/editor-768x384.png 768w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/editor-1536x767.png 1536w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/editor-2048x1023.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" data-id=\"218\" src=\"https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/export-1024x512.png\" alt=\"\" class=\"wp-image-218\" srcset=\"https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/export-1024x512.png 1024w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/export-300x150.png 300w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/export-768x384.png 768w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/export-1536x767.png 1536w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/export-2048x1023.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" data-id=\"219\" src=\"https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/graph-1024x512.png\" alt=\"\" class=\"wp-image-219\" srcset=\"https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/graph-1024x512.png 1024w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/graph-300x150.png 300w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/graph-768x384.png 768w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/graph-1536x767.png 1536w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/graph-2048x1023.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<figure class=\"wp-block-image size-large\"><img loading=\"lazy\" decoding=\"async\" width=\"1024\" height=\"512\" data-id=\"217\" src=\"https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/upload-1024x512.png\" alt=\"\" class=\"wp-image-217\" srcset=\"https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/upload-1024x512.png 1024w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/upload-300x150.png 300w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/upload-768x384.png 768w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/upload-1536x767.png 1536w, https:\/\/nils-becker.org\/wp-content\/uploads\/2026\/10\/upload-2048x1023.png 2048w\" sizes=\"auto, (max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n<\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Unter der Haube<\/h2>\n\n\n\n<p>Mein Werkzeug nutzt die TMDL-Dateien, die Power BI beim Speichern als Projekt (.pbip) anlegt. Im ersten Schritt liest ein eigens entwickelter TMDL-Parser dieses Microsoft-eigene Format ein \u2013 reines Python, keine Abh\u00e4ngigkeiten.<\/p>\n\n\n\n<p>Anschlie\u00dfend werden die Objekte aus den TMDL-Dateien auf die Ossie-Semantik abgebildet. Das Ganze funktioniert statisch und deterministisch: Dasselbe Modell ergibt immer dasselbe Ergebnis.<\/p>\n\n\n\n<p>Das Ossie-Modell kann nun mit einem der fertigen Konverter weiterverarbeitet werden. Die f\u00fcr Snowflake, Cube und Honeydew habe ich direkt in mein Projekt integriert. Der Databricks-Konverter ist leider in Java geschrieben. Damit das Werkzeug ohne Java auskommt, habe ich ihn nach Python portiert. Die Tests des Originals laufen auch gegen den Port, und die Ausgabe ist identisch mit der des Java-Konverters.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Grenzen von dem Ganzen<\/h2>\n\n\n\n<p><br>Das Werkzeug ist bisher mit einem kleinen Beispielmodell und den Testsuiten der Konverter gepr\u00fcft, nicht mit gro\u00dfen Produktivmodellen. Ein offener Punkt ist vor allem die \u00dcbersetzung von DAX nach SQL. Kennzahlen in DAX werden zwar mitgenommen, SQL-basierte Ziele wie Snowflake oder Databricks \u00fcberspringen sie aber. Die naheliegende L\u00f6sung w\u00e4re ein KI-Agent, der DAX nach SQL \u00fcbersetzt. Dabei bliebe allerdings der Determinismus auf der Strecke.<\/p>\n\n\n\n<p><br>Wer ein gr\u00f6\u00dferes Modell hat und es ausprobiert: Ich freue mich \u00fcber R\u00fcckmeldungen und Issues auf GitHub.<\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Lizenz<\/h2>\n\n\n\n<p>tmdl2ossie ist frei f\u00fcr private Nutzung, Forschung, Lehre und gemeinn\u00fctzige Organisationen (PolyForm Noncommercial 1.0.0). F\u00fcr den Einsatz in Unternehmen gibt es eine kommerzielle Lizenz; Anfragen gerne an mail(at)nils-becker.org.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>F\u00fcr viele Unternehmen ist Power BI der Dreh- und Angelpunkt, wenn es um Zahlen, Fakten und Entscheidungen geht. Dort wird die Bedeutung der Daten beschrieben, sie werden transformiert und ihre Zusammenh\u00e4nge modelliert. Diese Informationen bleiben allerdings oft isoliert im Microsoft-Universum. M\u00f6chte man Agenten nutzen, um Insights zu generieren, muss man sie erst aufw\u00e4ndig f\u00fcr den [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[26,22,24,30,28],"class_list":["post-216","post","type-post","status-publish","format-standard","hentry","category-uncategorized","tag-ossie","tag-power-bi","tag-semantik","tag-semantischer-layer","tag-tmdl"],"_links":{"self":[{"href":"https:\/\/nils-becker.org\/index.php?rest_route=\/wp\/v2\/posts\/216","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/nils-becker.org\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/nils-becker.org\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/nils-becker.org\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/nils-becker.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=216"}],"version-history":[{"count":2,"href":"https:\/\/nils-becker.org\/index.php?rest_route=\/wp\/v2\/posts\/216\/revisions"}],"predecessor-version":[{"id":223,"href":"https:\/\/nils-becker.org\/index.php?rest_route=\/wp\/v2\/posts\/216\/revisions\/223"}],"wp:attachment":[{"href":"https:\/\/nils-becker.org\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=216"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/nils-becker.org\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=216"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/nils-becker.org\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=216"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}