mardi 21 février 2012

PowerShell, c'est la vie!

Commes mes fans se sont plaints  hier il y a peu du manque d'activité sur ce blog, voici un petit post pour les réconforter (si je n'ai rien publié depuis, c'est parce que je suis sur l'écriture d'un livre sur SSIS et que je n'ai pas grand chose de nouveau à dire : on se cherche des excuses comme on peut).

J'ai dû mettre en place aujourd'hui il y a peu la création automatique de partition sur SSAS. Ce n'est pas la première fois que je fais ça mais ici, la manière est bien différente car le contexte le requérait. D'habitude, la création de ces partitions s'inscrit dans un process global de mise à jour d'une partie du SID (ETL, DWH, etc...) : le tout est géré par un service Windows custom. Dans ce cas précis, l'AMO est idéal.

Dans le cas du jour, la création de partition est décoréllée de tout cela. Elle doit avoir lieu en fin d'année, vers le mois de Novembre afin d'anticiper l'arrivée des chiffres de l'année suivante. Bref, je me retrouve avec les méthodes que je connais :
- du XMLA
- de l'AMO
- un lot SSIS

Pour le XMLA, ça ne me convient pas car je vais avoir des expressions conditionnelles dans mon code.
L'AMO, c'est bien, mais super lourd pour le déploiement. C'est sortir l'artillerie pour juste créer des partitions : à mon avis une perte de temps.
Le lots SSIS avec un component script : la méthode la plus abordable à mon avis, mais chez le client en question, les normes de développement des lots sont lourdes et je ne me voyais pas passer ma journée à lire les docs pour coller à leurs spécifications juste pour un dtsx.

Je n'ai pas envie de choisir entre ces méthodes en fonction de leurs inconvénients. Du coup, j'ai opté pour Powershell. C'est la première fois que je fais quelque chose de concret avec et je dois dire que c'est super agréable. On peut presque tout faire comme en AMO mais dans un fichier de script basique.

L'exemple ci dessous permet de créer des partitions automatiquement pour tous les groupes de mesures en fonction de certains prédicats :
- le partitionnement se fait sur l'année
- le nommage des partitions est pertinent (le nom contient à minima l'année, idéalement à la fin du nom de la partition)
- le formatage de la clé de temps est intelligent (20110101 au hasard)
- une partition a été initialement définie dans le groupe de mesure afin de se baser dessus pour créer celles jusqu'à l'année en cours


$serverName = "MonServeur\MonInstance"
$dbName = "Adventure Works"
$cubeName = "Adventure Works"
$yearTarget=2013

[System.Reflection.Assembly]::LoadwithpartialName("Microsoft.AnalysisServices") > $null
$svr = New-Object Microsoft.AnalysisServices.Server
$svr.Connect($serverName)
$cub = $svr.Databases.Item($dbNamed).Cubes.FindByName($cubeName)
#on parcourt les groupes de mesures
foreach ($mg in $cub.MeasureGroups)
{
$part=$mg.Partitions
#le nommage des partitions est MonGroupeDeMesure2012. Les partitions étant créées dans l'ordre
chronologique, on ne récupère l'année que dans le nom de la dernière
$yearInit= [int] ( $part[$part.Count-1].name.substring($part[$part.Count-1].name.length-4, 4))+1

while($yearInit -le $yearTarget)
{
#on définit la requête source de la partition
$query="SELECT MesColonnes FROM MaTable WHERE MaDateKey BETWEEN " + ([string]$yearInit)+"0101 AND
"+([string]$yearInit)+"1231"
#le slice correspondant
$slice="[DimTime].[Calendar Hierarchy].[Year].&["+([string]$yearInit)+"]"
#on ajoute la partition à la définition de mon cube
$partadd=$mg.Partitions.add($mg.name+ " " + $yearInit)
$partadd.Slice=$slice
#j'instancie un objet de type QueryBinding pour la définition de la source : bien faire attention dans le cas où la base AS possède plusieurs DataSource de choisir la bonne dans la collection
$partadd.Source= New-Object Microsoft.AnalysisServices.QueryBinding ($svr.Databases.Item
($sourceDB).DataSources[0].ID ,$query)

#ne pas oublier cette commande qui permet de valider les changements dans la structure, c'est une sorte
de commit
$partadd.Update()
$partadd.Process()
$yearInit++
}
}


Ce code n'a pas de prétention particulière, c'est juste pour donner une base de syntaxe et vous convaincre que le PowerShell, c'est la vie.

4 commentaires:

  1. Merci Patrice, tes fans sont satisfaits :)

    Point de vue implémentation, c'est un morceau de code à exécuter chaque année - donc à planifier comment? - ou tu le ferais une bonne fois pour toutes pour les 25 prochaines année?

    RépondreSupprimer
  2. Effectivement, j'aurai pu me simplifier la vie et créer toutes les partitions pour les X prochaines années, mais ça ne permet pas d'être souple en cas de redéploiement du cube (les partitions seraient alors absentes) et le positionnement du slice sur un membre qui n'existe pas encore dans la dimension fait échouer le processing de la partition. Pour une précision sur le contexte de ce bout de code, chez le client personne n'a la maîtrise de la base AS, donc un processnig qui échoue et c'est la panique.
    Il est planifié via un job SQL agent tous les ans.

    RépondreSupprimer
  3. Sympa, moi je le trouve plus qu'utilisable ton truc :)! A la limite le seul truc qu'on pourrait modifier c'est de cloner la partoche sur la dernière existante pour pas s'embêter avec les bindings, et de stocker les métadonnées de la partition dans les Annotations plutot que dans le nom. Après tu appelles ça HarelProcess tu le vends et tu deviens riche.

    RépondreSupprimer
  4. Yep, mais c'était au cas où aucune partition n'était définie, sinon effectivement, il suffit de cloner.
    Par contre c'est génial l'idée des annotations pour gérer les métadonnées : avec du recul il me semble que tu m'en avais déjà parlé. J'y penserai pour les prochaines implémentations.

    RépondreSupprimer