mercredi 13 février 2013

SSAS 2012 : Conversion entre modèles tabulaire et multidimensionnel (1ère partie)

Si vous avez suivi, lors des Techdays 2013, l'excellente session sur Analysis Services 2012 animée par Messieurs François Jehl et Romuald Coutaud, vous avez pu constater que le parser XMLA pour les modèles tabulaires et multidimensionnelles est le même.
En effet, ils ont pu intégrer des actions ou des traductions sur des modèles tabulaires, bien que ces derniers ne proposent pas de le faire. (à noter que la version de BIDS Helper pour SSAS Tabular permet de le faire de manière graphique).

L'idée de départ de cet article est donc la suivante : que va-t-il se passer si j'exécute un script XMLA de create database d'un modèle tabulaire sur un serveur Analysis Services multidimensionnel. Et surtout l'implication logique : vais-je pouvoir convertir mon modèle tabulaire en modèle multidimensionnel ? Ou que va-t-il me manquer pour convertir les modèles ?

Première partie pour l'article, car on va rester sur un modèle tabulaire simple et sur une conversion tabulaire vers multidimensionnel. Il mériterait une deuxième partie pour des modèles plus complexes et une conversion inverse.

On a posé le contexte, on peut donc partir sur notre modèle tabulaire très simple :

  • 1 table de fait (dans laquelle j'ai créé une mesure qui somme mon CA)
  • 1 dimension Client
  • 1 dimension Produit
  • 1 dimension Magasin

Si je me connecte à mon instance SSAS Tabulaire, je peux bien browser mon cube sans souci :


Générons maintenant l'XMLA de création de la database et essayons de l'exécuter sur mon instance multidimensionnelle. J'obtiens l'erreur (attendue) m'indiquant que mon serveur n'est pas un serveur tabulaire :


Et c'est ici qu'on va commencer à mettre les mains dans le XMLA (de manière assez superficielle malgré tout). J'ai en effet bon espoir que cette erreur ne soit due qu'au choix de la valeur InMemory pour le paramètre StorageEngineUsed dans le noeud Database.
Et c'est bien le cas, car en commentant cette ligne, j'obtiens une erreur différente :


L'erreur nouvellement générée est également due à un paramètre de stockage défini à "InMemory", mais cette fois au niveau dimension. On va donc commenter toutes les lignes propres au "StorageMode".
Ça a l'air de marcher car j'obtiens une nouvelle erreur qui ne dépend pas du stockage : Le paramètre UnknownMember ne peut pas être à AutomaticNull sauf pour du modèle tabulaire :


Soit, on va donc continuer sur le même principe et commenter les lignes relatives au UnknownMember. On continue d'avancer car j'obtiens une nouvelle erreur. Celle-ci est intéressante, car elle dit que je ne peux pas avoir de relation au niveau dimension quand je suis en modèle traditionnel. 

Si j'en crois la suite de l'expérience, ça fait surtout sauter les relations qu'on définit au niveau du datasource view, car on verra qu'on obtient bien un Dimension Usage au niveau du cube.

On va commenter les lignes propres aux relations de dimensions, et on continue notre expérience  Evidemment, on obtient une nouvelle erreur. C'est encore un paramètre propre au tabulaire sur le partage de stockage entre dimension et groupe de mesures. On commente et on continue. 
Et là miracle (miracle à moitié, vu qu'en fait j'ai désactivé tout ce qui était propre à du tabulaire) mon XMLA s'est exécuté sans nouvelle erreur. Et j'ai même un cube sur mon serveur multidimensionnel :


J'ai donc réussi la première étape : je vais pouvoir ouvrir mon cube dans SSDT en mode de création multidimensionnel.

Première constatation, j'ai un datasource view fonctionnel sans relation, j'ai des dimensions pour toutes mes tables (il a même gardé la hiérarchie que j'avais créée sur ma dimension Client) et j'ai un cube avec un groupe de mesures également pour toutes les tables (Si on examine le XMLA en tabulaire, on constate bien qu'il crée une dimension et un groupe de mesures par table)
Deuxième constatation, dans toutes mes dimensions il a créé un attribut RowNumber comme clé de dimension (en effet, Tabular crée ce membre par défaut sur chaque table pour garantir l'unicité des lignes, on peut le constater dans le XMLA. Cet attribut a pour KeyColumn et NameColumn RowNumberBinding qui n'est pas supporté en multidimensionnel), mais je ne l'ai pas dans mon datasource view. Les mesures (ici je n'avais créé que le total des CA dans ma table Ventes, se retrouve dans le script MDX. Le Dimension Usage de mon cube contient des relations de type Fact pour chaque groupe de mesure et des relations référencées pour ma table centrale.


A partir de là, il est difficile de continuer sans tout refaire, car 
- si l'on change de clé de dimensions, on casse le dimension usage. Et on est obligé de retoucher aux clés

Cependant le rajout de la fonctionnalité RowNumberBinding au niveau des KeyColumn dans les propriétés des attributs semblerait suffisant à le rendre fonctionnel.

Pour voir ce qui pourrait manquer encore, j'ai continué en reconstruisant les dimensions avec les Id existant dans les tables, et en redéfinissant le Dimension Usage.

La dernière erreur au process du cube vient de notre champ calculé (qui je le rappelle était une bête somme de CA). Toutes les mesures créées en Tabulaire sont en fait exécutées dans le MdxScript. J'obtiens une erreur de syntaxe, ce qui est assez paradoxale quand on sait que l'inverse est possible (écrire du MDX discrètement dans un modèle tabulaire, ce qu'avait fait François Jehl dans sa session au Journées SQL Server 2011).


En ré-écrivant la formule en MDX (en oubliant pas de changer le "=" par "AS"), ça processe, mais ce n'était plus très important. Le principal étant d'avoir pointer du doigt les limites de la conversion.


En conclusion, on sent qu'il ne faudrait pas grand chose pour faire en natif un convertisseur de modèle de tabulaire vers multidimensionnel, car les limites atteintes ne sont pas vraiment rédhibitoires :

- on a tout ce qui concerne le stockage InMemory : il suffirait que le convertisseur le fasse sauter, ou mieux l'intègre au mode multidimensionnel

- ce qui concerne les relations entre dimensions : j'avoue que je n'ai pas vu l'utilité en mode multidimensionnel. Je pensais au début qu'il s'agissait du dimension usage du cube, mais apparemment ce n'est pas ça car il y en a un de défini à l'ouverture

- l'ajout d'une option sur les KeyColumn des attributs (RowNumberBinding) qui permettrait de conserver le dimension usage, ou pourquoi pas un assistant pour regénérer les relations lors de la conversion.

- et pouvoir écrire du DAX dans le MdxScript : les modèles tabulaires pouvant être attaqués en MDX, c'est bien qu'il existe un interpréteur.










4 commentaires:

  1. Chouette post mon David, tu as été plus loin que moi, tu persévères! Pour ton erreur de MdxScript, je pense que le problème vient du fait que le Alter XMLA du script en fin de process de cube ne réagit pas comme les clients, et par conséquent ne supporte QUE du MDX.

    Il faudrait tester un Alter XMLA avec du DAX pour vérifier l'assertion. (http://www.purplefrogsystems.com/blog/2010/10/deploying-mdx-calculation-scripts-with-xmla/)

    Si ça se confirme, ça voudrait dire qu'il faudrait modifier la commande Alter. Ou alors désactiver le DAX, en utilisant le même principe que le MdxScript Parser en RegEx de Darren Gosbell (http://geekswithblogs.net/darrengosbell/archive/2006/08/10/87561.aspx) en commentant ce qui est du DAX (je pense que les := sont assez spécifiques).
    En tous les cas gg mon pote!

    PS: Ah et pour les RowNumberBinding on pourrait altérer la DSV pour crééer des colonnes calculées ROW_NUMBER(OVER PARTITION BY (SELECT 0)) bindées sur ces colonnes là non?

    RépondreSupprimer
  2. J'ai pensé à générer l'auto-incrément dans la DSV (mais j'ai complètement zappé un row_number over partition par contre, honte à moi), mais j'ai peur que ça fasse n'importe quoi au niveau des liens avec la table centrale en fait. C'est peut-être là que les relations entre dimensions que j'ai désactivées sont importantes.

    RépondreSupprimer
  3. Mais en fait, vu que c'est utilisé uniquement pour des relations sur des tables avec elles-mêmes, on s'en moque. Donc effectivement, ça se tente

    RépondreSupprimer
  4. Bon après test, on peut créer les champs RowNumber dans le datasource view, mais le dimension usage du cube ne se rafraichit pas si on revalide pas les relations. Ce qui est destructeur et revient à le recréer.

    RépondreSupprimer