Vagrant : Le développement sur VM

Nous nous sommes tous heurter à la difficulté de mettre en place son environnement de développement, surtout dans le cadre de la mise en place d’une plateforme homogène entre plusieurs postes sans ajout de logiciels tiers et le plus proche possible de l’environnement de production.

– Exemple : une plateforme PHP, MySQL, Postgres dans le cadre de la migration d’un application PHP de MySQL vers Postgres.

Au fil de mes recherches sur le net et surement avec beaucoup de retard je suis tombé sur Vagrant. (https://vagrantup.com)

Vagrant a pour but de rendre plus simple l’utilisation de machine virtuelle dans le cadre de l’environnement de développement. On peut le dire le but est atteint pour la facilité de développement entre la machine hôte et la VM.

Le plus compliqué avec Vagrant va être de créer sa propre machine proche des habitudes de développement de l’équipe. Car Vagrant n’est pas là pour cela, il est juste là pour le montage de la machine et son utilisation au quotidien.

Heureusement, la communauté est très active sur le sujet et des outils sont là pour simplifier la création de sa propre Box avec quelques connaissances. Je pense notamment à Veewee (https://github.com/jedi4ever/veewee) qui est vraiment un outil à s’accaparer pour la création de sa propre Box, n’hésitez pas à dupliquer le projet et pusher vos modifications pour mettre à disposition vos Box, c’est d’ailleurs ce que je vais faire.

Mais, ne vous inquiétez pas, je vous prépare un petit tutoriel pour l’utilisation de Veewee avec une Box en français.

Et, important à savoir tout cela, c’est en Ruby.

Tip SQL: pourquoi Migrer de MySQL vers PostgreSQL?

Comment dire….
quand on a fini de jouer avec MySQL et que l’on veut passer en production, on cherche un moteur SQL de pros.
C’est un peu comme quand tu as cassé ta perceuse achetée chez Lidl (en promo !) et que tu bave devant la bosh dans le rayon chez casto…
Moi, mon vendeur, il m’a fait l’article et je viens tout juste de déballer le paquet:  PostgreSQL 9.
Avec ça, j’ai bien envie de percer des trous partout dans mes serveurs…
Bon, je m’emporte:

  1. MySQL c’est quand même bien!
  2. je fais toujours des courses chez lidl
  3. même facebook utilise la perceuse pas cher de chez Lidl ( MySQL)
C’est quoi les avantages de MySQL ?
  • d’abord le moteur est facilement configurable
  • l’outil de requêtage MysqlWorkBench est pas mal –  je dirais même , il s’approche de ce que fait Microsoft.
  • la doc en ligne est ok
  • la communauté est active
Mais quand on se penche un peu sur le bestiau, il y a des trucs qui me gênent : 
  1. la sauvegarde incrémentale InnoDB => pas disponible en mode gratos. Vas donc sauvegarder 400 Go d’un serveur sur-sollicité….
  2. le mode mono-thread sur les requêtes : Fais donc une auto-jointure sur une table de 25 millions d’enregistrements; ben, tu peux pas Mr patate !! en tout cas pas avec ton kimsuffi à  trois euros six sous. 
  3. pas de sharding (répartition de données)  sauf à  l’implémenter côté applicatif; Bref, réinventer ce foutu fil à couper le beurre que j’ai eu tant de mal à réinventer les 50 dernières fois.
  4. et puis j’ai aussi envie de changer les rideaux , donc je veux ma nouvelle perceuse !
Alors quelle perceuse doit-je prendre ? en général, le vendeur vous demande ce que vous allez en faire.
Moi, je veux :
  1. une Bdd gratos
  2. facile à  administrer 
  3. grosse volumétrie
  4. qui va se comporter en entrepôt de données ( peu de transactions, beaucoup de lectures, beaucoup de calculs)
  5. compatible avec ce qui a déjà  été implémenté côté applicatif
  6. le plus proche possible de l’ANSI SQL 92 ( utilisation des instructions JOIN )
Vous savez maintenant ce que j’ai choisi comme Base de données….

Et vous, vous pensez quoi des avantages de l’un et de l’autre ?

Tip MySQL: restaurer une table à partir d’une sauvegarde complète

En tant qu’administrateur de base de données MySQL, vous avez surement mis en place une  politique de sauvegarde des bases de données qui permet de sauvegarder toutes les bases installées.
Pour ma part j’utilise automysqlbackup qui permet de faire une rotation des bases de données.
Or ce logiciel sauvegarde l’ensemble des bases dans un seul et même fichier.

Imaginons maintenant qu’un développeur vienne vous dire qu’il a détruit les données d’une table et qu’il faudrait restaurer uniquement cette table à  partir des données de la veille.

La première chose que vous faites et de croiser les doigts en allant regarder dans le répertoire de sauvegarde s’il y a bien un dump des bases.
Ensuite, vous essayez de localiser la table en question avec votre éditeur préféré pour faire un copier coller dans l’IHM MySQL Workbench.
Problème:  le fichier de dump fait 20 Go!
même vi a du mal à  ouvrir le fichier…
Vous devez donc vous pencher sur les capacité du programme sed dont voici une bonne introduction.

L’explication suivante s’appuie sur un exemple précis:
 restaurer la table comments qui appartient à  la base de donées facebook.

Identification des patterns

Si on regarde de plus près le type de sauvegarde qu’automysqlbackup effectue ( grâce au programme mysqldump), on remarque plusieurs choses :

  • avant  chaque création de base de données on retrouve un commentaire.Exemple : 


— Current Database: `facebook`

CREATE DATABASE /*!32312 IF NOT EXISTS*/ `facebook` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `facebook`;

  • pour chaque table , la table est détruite ( commande DROP)

DROP TABLE IF EXISTS `comment`;

  • la table est crée, verrouillée, les données sont insérées puis enfin la table est déverrouillée : 

UNLOCK TABLES;

On peut donc en déduire une stratégie :

  1. d’abord extraire la base de données cherchée et la sauvegarder dans un fichier
  2. ensuite extraire la table  cherchée et la sauvegarder dans un fichier

Extraction de la Base  de données 

On va donc utiliser le pattern « Current Database » pour  extraire uniquement la base de données facebook:

sed -n -e ‘/Current Database: .facebook/,/Current Database/p’ mon_fichier_de_dump.sql > facebook.sql

cette commande va extraire à  partir du fichier mon_fichier_de_dump.sql tout ce qui se trouve entre le texte « Current Database: `facebook` » et le prochain texte « Current Database: »  et sauvegarder dans le fichier facebook.sql.

l’option -n avec l’option /p  permet à  sed de n’écrire que les lignes comprises entre les deux patterns.
l’option -e

Extraction de la table

Une fois que la base de données est extraite, on peut maintenant extraire la table comment :

sed -n -e ‘/DROP TABLE.*`comment`/,/UNLOCK TABLES/p’ facebook.sql > comment.sql

Restauration de la table

 Il suffit ensuite de restaurer la table comments dans votre serveur de base de données avec la commande mysql:

mysql -h myserver.mynetwork.com  -u root -pmyPassword -D facebook< facebook.sql

Evidemment, vous pourrez modifier

Tip SQL/MDX: chercher le top X pour chaque groupe

Une des demandes les plus récurrentes, lorsque l’on fait des tableaux de bord c’est de retrouver les « 10 meilleurs clients par zone commerciale », ou encore les « 10 plus gros contributeurs facebook par page » .

Aujourd’hui je vous propose 3 façons de résoudre cette problématique en SQL, selon le système  utilisé.
Imaginons la table suivante:

Create   TABLE Contribs (id_page int, page varchar(50), fan varchar(50) , contribs INT);

INSERT INTO Contribs VALUES (2, ‘françois Hollande’, ‘toto’ , 65);
INSERT INTO Contribs VALUES (2,’françois Hollande’, ‘titi’ , 50);
INSERT INTO Contribs VALUES  (2,’françois Hollande’, ‘tutu’ , 30);
INSERT INTO Contribs VALUES (2,’françois Hollande’, ‘tata’ , 20);
INSERT INTO Contribs VALUES  (8,’Nicolas Sarkozy’, ‘toto’ , 5);
INSERT INTO Contribs VALUES  (8,’Nicolas Sarkozy’, ‘titi’ , 41);
INSERT INTO Contribs VALUES   (8,’Nicolas Sarkozy’, ‘tutu’ , 35);
INSERT INTO Contribs VALUES  (8,’Nicolas Sarkozy’, ‘tata’ , 66);
INSERT INTO Contribs VALUES  (8,’Nicolas Sarkozy’, ‘tete’ , 67);
INSERT INTO Contribs VALUES  (24,’Marine Le Pen’, ‘toto’ , 35);
INSERT INTO Contribs VALUES  (24,’Marine Le Pen’, ‘tata’ , 15);
INSERT INTO Contribs VALUES  (24,’Marine Le Pen’, ‘titi’ , 5);
INSERT INTO Contribs VALUES  (24,’Marine Le Pen’, ‘tutu’ , 3);
INSERT INTO Contribs VALUES  (24,’Marine Le Pen’, ‘zozo’ , 2);
INSERT INTO Contribs VALUES  (8,’Nicolas Sarkozy’, ‘zizi’ , 69);
INSERT INTO Contribs VALUES  (8,’Nicolas Sarkozy’, ‘zaza’ , 100);
INSERT INTO Contribs VALUES  (8,’Nicolas Sarkozy’, ‘zuzu’ , 1230);

Cette table correspond à la liste des fans ( champ fan) de pages page facebook avec la liste

Sql Server:

Bon là c’est tellement simple que s’en est bête… on va utiliser la fonction RANK, introduite avec la version 2005
la fonction RANK() , permet de calculer un auto-incrément lors d’une requête.
Par exemple:

select *   from  ( select page, fan, contribs, RANK() over ( Partition by page order by contribs DESC)  as Rank
from  Contribs ) as TEMP
where Rank <=10;

MySql:

Bon, là  on va utiliser une fonctionnalité que j’appellerais « procédurale »de Mysql et non ensembliste.
Je m’explique:
au sein même de la requête, on utilise l’incrémentation au fur et à  mesure que le moteur lit et ordonne les lignes qu’il trouve.
On doit d’abord ordonner les contributeurs  par page:
select page, fans, contribs from Contribs  order by page, contribs DESC;

On introduit l’incrémentation au travers d’une variable : @count.
Dès que le moteur de requête change de  page, il est nécessaire de remettre à  zéro la variable @count.
L’indication du changement de page se fera avec la variable : @oldPage

select  @oldPage := 0 , @count := 0;
SELECT
    @count := IF(id_page <=> @oldPage, @count+1, 0) as mycount,
    @oldPage := id_page,
    page,
    fan,
    contribs
FROM Contribs
ORDER BY id_page, contribs DESC;

Il suffit ensuite de filtrer :

select fan,page, contribs  from (SELECT
    @count := IF(id_page <=> @oldPage, @count+1, 0) as mycount,
    @oldPage := id_page,
    page,
    fan,
    contribs
FROM Contribs
ORDER BY id_page, contribs DESC)  t
where mycount<=10;

ANSI SQL:

On doit toujours revenir aux bases…
Sachant que le langage SQL ne sait pas compter la position dans le résultat, il faut trouver autre chose.
On peut essayer d’imaginer d’extraire pour chaque contribution ( contribs)  le sous-ensemble des contributions supérieures et inférieures.

Pour chaque contribs, on peut extraire le sous ensemble des contribs supérieures:

select C2.contribs, C1.contribs
from  Contribs as C1
join Contribs as C2 on C1.contribs >= C2.contribs

On veut maintenant n’avoir que les contribs qui n’ont que trois contribs qui lui sont supérieurs. On va compter pour chaque sous ensemble le nombre distinct de contribs  :

select min(C1.contribs)
from  Contribs as C1
join Contribs as C2 on C1.contribs >= C2.contribs
group by C2.contribs
having count(DISTINCT C1.contribs) <=3;

Avec cette requête, on récupère ainsi les trois plus hautes contribs c’est à  dire 69,100 et 1230

Enfin, on veut faire le regroupement par page :

select min(C1.contribs) , C1.page
from  Contribs as C1
join Contribs as C2 on C1.contribs >= C2.contribs and C1.id_page = C2.id_page
group by C2.contribs, C1.page
having count(DISTINCT C1.contribs) <=3;

Il ne reste plus qu’à  injecter les noms des contributeurs :

select c.*  from  Contribs c
JOIN
(select min(C1.contribs) as contribs, C1.id_page
from  Contribs as C1
join Contribs as C2 on C1.contribs >= C2.contribs and C1.id_page = C2.id_page
group by C2.contribs, C1.id_page
having count(DISTINCT C1.contribs) <=3) t
on t.id_page = c.id_page and c.contribs = t.contribs;

Et voilà:  on récupère les trois meilleurs contributeurs par page!!
C’est d’ailleurs la requête la plus élégante et celle qui fonctionnera partout.

MDX:

Alors là, le système est très simple  avec l’instruction TopCount puisqu’il permet de faire le filtre pour chaque sous-ensemble.
Par exemple, on crée un « set » des clients à qui on a le plus vendu sur 2012, puis on peut l’utiliser  en dans une requête MDX:

with set [Top10cust] as ‘TopCount({[Client].[ClientName].Members}, 10.0, ([Time].[2012], [Measures].[sales]))’
member [Measures].[Ventes] as ‘[Measures].[sales]’
select {[Measures].[Ventes]} on COLUMNS,
 {[Top10cust]}  ON ROWS
from [Commercial]

Pentaho:calcul du pipe commercial en MDX avec Mondrian – partie 3

Dans mon précédent post j’avais résolu le problème du calcul du pipe pour le mois de janvier.

On avait introduit deux dimensions [TimeStart] et [TimeEnd] qui permettait de faire des filtres sur les dates de début et de fin des devis.
La formule initiale qui permet de savoir le pipe pour le mois de janvier 2011, est celle-ci :
with MEMBER Measures.x as ‘
SUM( NonEmptyCrossJoin(
OpeningPeriod([TimeStart].[Month],[TimeStart].firstchild):[TimeStart].[2011].[Q1].[January],
[TimeEnd].[2011].[Q1].[January]:Closingperiod([TimeEnd].[Month],[TimeEnd].lastchild ) )
, [Measures].[Montant] )’
Select Measures.x on 0
Le problème, maintenant est que l’on doit trouver un moyen pour faire cela pour tous les mois…
On sait que contrairement à MS OLAP, Mondrian n’implémente pas la fonction linkMember et donc ne peut pas lier les deux dimensions TimeStart et TimeEnd entre elles.
Imaginons que l’on ait d’une part la liste de tous les devis ouvert et d’autre part tous les devis fermés, on pourrait ainsi savoir pour chaque période la somme des devis qui ont été traités.
En SQL ça donne :
select T.DateTypeKey , T.TimeKey , T.TimeKeyStart,
T.TimeKeyEnd,
T.montant
from
(select distinct ‘Ouvert’ as DateTypeKey , TimeKeyStart as TimeKey, TimeKeyStart, TimeKeyEnd, montant from FactPipe
UNION ALL
select distinct ‘Cloturé’ as DateTypeKey , TimeKeyEnd as TimeKey, TimeKeyStart, TimeKeyEnd,montant from FactPipe
) T
Imaginons que cette requete soit sauvegardée entant que vue que l’on va appeler viewPipe
Si, en SQL je veux savoir le montant total des devis ouvert jusqu’au 1er janvier 2011, je dois faire cette requête:

select sum(montant) from viewPipe where TimeKey <= '2011/01/01' and DateTypeKey = 'Ouvert'

De même, je veux savoir la somme des devis clôturés jusqu’au 1er janvier 2011, la requête est sensiblement la même :

select sum(montant) from viewPipe where TimeKey <= '2011/01/01' and DateTypeKey = 'Cloturé'

Côté MDX- Mondrian, on va donc introduire une nouvelle dimension que je vais appeler [DateType] qui va permettre de gérer ce fameux status ouvert/clôturé
La particularité de cette nouvelle dimension est qu’elle

Lion : Upgrade vs Clean Install

Après deux mois d’utilisation de Lion, en upgrade, je viens de faire une clean install. 

 Pourquoi ?

Simple, le système me paraissait assez instable, et surtout lent par moment, voir extrêmement lent surtout au reboot environ 56s de boot. 

Pourquoi, pas avant ?

J’ai opté pour la facilité, en effet, l’upgrade est d’une simplicité et surtout vraiment rapide. 
De plus, j’avais un peu peur quand à la récupération de mes données, possédant une bibliothèque photo d’environ 90 Go, vous comprendrez ma crainte, mais bon time machine est là. 
Comment procéder ? 
D’abord, je conseille à tout le monde de se faire un clé USB en suivant le tutoriel de l’outil Lion Disk Maker : https://blog.gete.net/lion-diskmaker/.
Cet outil est très bien fait et permet de préparer la réinstallation de manière plus efficace que la clé de récupération Apple.
Une fois, la clé prête : 
  1. Une vérification du disque dur avec « Utilitaire de disque »
  2. Backup avec Time Machine sur un disque vierge (conseil la backup courante suffit)
  3. Redémarrage de l’ordinateur en utilisant la touche Alt afin de pouvoir choisir la clé USB qui doit évidement être inséré dans un port USB
  4. Choisir la clé pour le redémarrage 

Vous allez alors arriver sur l’écran suivant : 
Il va alors dans le but d’une installation propre falloir suivre les étapes suivantes : 
  1. Aller dans l’utilitaire de disque et effacer la partition principale, généralement « Macintosh HD »
  2. Surtout choisir en type fichier celui proposé : « MacOS etendu (journalisé) » 
  3. Une fois l’opération effectuée – fermer l’Utilitaire avec la croix rouge vous revenez alors sur l’écran ci-dessus
  4. A partir de là lancer l’installation en utilisant « Réinstaller Mac OS X »
  5. Suivez les instructions 
Pour la récupération des données, personnellement, j’ai utilisé Time Machine mais pas l’Utilitaire de migration.
J’ai raccordé le disque utilisé pour ma sauvegarde précédemment et je lui ai dit que c’était un disque time machine, j’ai alors arrêté la première sauvegarde et je suis entré dans Time Machine pour ne récupérer que mes fichiers de données et non les paramètres.
Résultat visible : 
  • Boot plus rapide 40s vs 56s (16s c’est toujours bon à prendre)
  • Fermeture plus rapide : 5s vs 15s
  • Application plus fluide surtout visible avec iTunes et iPhoto dont la bibliothèque pèse plus de 90Go.
  • Lancement des applications presque immédiatement.
Bref, aucun regret, si ce n’est un peu de temps, bien que l’ordinateur a travaillé tout seul la plupart du temps, début de la sauvegarde 9:30 fin vers 13:00.
Début de l’installation 13:15 fin vers 13:30
Début de la restauration incluant l’installation des mises à jour de l’OS 13:45 fin vers 16:30.
Il faut là l’admettre quelque soit l’OS une clean install prend du temps surtout à cause des opérations de sauvegarde et de restauration.

RVM : Ruby Version Manager

Quand on essaye de développer sur Ruby, on se tourne tout de suite vers une machine virtuel sous Linux, non pas que Ruby pourrissent le système mais plutôt parce que sous windows il est beaucoup moins performant (c’est mon point de vue).
Quand on essaye de développer sur Ruby sous Linux, on entend rapidement parler de RVM (Ruby Version Manager).
RVM, c’est vraiment le paradis pour le développeur Ruby qui veut tester sur plusieurs types d’environnement (Ruby 1.8.7, 1.9.2, JRuby….), cet outil permet de switcher d’un environnement à un autre sans trop d’effort.
Il est hors de question de vous faire une fiche sur comment installer RVM, il y en a des très bonnes sur le net en Anglais certes et particulièrement https://pragmaticstudio.com/blog/2010/9/23/install-rails-ruby-mac qui marche exactement pareil sur ubuntu.
Je ne conseille pas l’installation en sudo qui n’est à mon avis pas utile.
Vous l’avez compris RVM est un outil indispensable pour les développeurs Ruby & Ruby on Rails, cependant, voici une liste de petits tips & tricks intéressant :
– Si lors de l’utilisation de « rake » vous rencontrez l’erreur « RakeFileUtils rake aborted », n’hésitez à utiliser le lien suivant : https://joneslee85.wordpress.com/2011/07/29/howto-fix-rake-0-9-2-to-work-with-ruby-1-9-2-under-rvm/
– uninitialized constant Rake::DSL in Ruby Gem, l’ajout de :
require 'rake/dsl_definition'
Dans le Rakefile
– vous rencontrez une erreur sur openssl avec Ruby on Rails, les commandes suivantes :
rvm pkg install openssl
rvm remove 1.9.2 --with-openssl-dir=$HOME/.rvm/usr
rvm install 1.9.2
A priori, vous avez là les principaux problèmes que l’on peut résoudre rapidement.

Time Machine : la sauvegarde pour les nuls

Ce titre, un brin péjoratif, reflète bien le fait que c’est un outil de sauvegarde d’une simplicité étonnante.
Depuis, Leopard (10.5), Mac OSX a introduit ce nouveau système de sauvegarde automatique. Pour l’utiliser un disque dur USB vierge qu’il faut formater en HFS+, dès le branchement du disque Time Machine vous demande si vous souhaitez l’utiliser comme disque de sauvegarde et commence la première synchronisation qui peut durer un moment en fonction des données de votre disque principal.
Avec Time Machine, Apple a prévu tous les cas de pertes de données effacement accidentel, crash disque… En effet, la restauration après réinstallation est automatiquement proposée.
Time Machine sauvegarde les changements toutes les heures. Si le disque sature il supprime les sauvegardes les plus anciennes avec ou sans avertissement.
Alors, vous pourrez me lancer que Windows 7 propose également cela oui c’est vrai mais je trouve l’outil moins performant sur trois points :
– Il faut soit même programmer ce que l’on veut sauvegarder et sur quel fréquence.
– La restauration n’est pas si visuellement simple.
– Il ne supprime pas de lui-même les sauvegardes les plus anciennes.
Alors, oui, je maintiens mon titre, Time Machine est vraiment la sauvegarde pour les nuls, cependant, il faut quand même que je le précise, il existe sous Linux un équivalent sympa et assez efficace mais un peu plus proche de la solution Windows : « Back in Time ».
La sauvegarde est un des points importants des futurs OS, en effet, les données numériques ont le vilain défaut de disparaître facilement, il faut donc que l’OS propose la solution la plus simple et efficace pour l’utilisateur avec une interface plaisante pour ne pas être rebuter par la mise en place de cette solution.

Pentaho:calcul du pipe commercial en MDX avec Mondrian – partie 2

Dans mon précédent post j’ai posé le problème du calcul par périodes du pipe commercial.


Maintenant que l’on a la solution en SQL, on pourra valider la requête MDX.

On voudrait retranscrire ce que l’on a fait en SQL c’est à dire sélectionner les devis dont la date de devis est inférieur ( ou égal) à la fin de la période et la date de commande ou d’expiration supérieure (ou égale) au début de la période

pour rappel la requête SQL est la suivante :

… where (datedevis <= "31/01/2011") and(datecommande >= « 01/01/2011 » );

Premier essai MDX: Modélisation du cube

Imaginons que l’on ai créé un cube Mondrian basé sur la table des devis en sachant

que la table contient deux dates ( date de devis et date de commande)

Il nous faut donc deux dimensions de temps liées ces deux dates

On a donc les dimensions suivantes [TimeStart] et [TimeEnd]

Ces dimensions ne sont que des dimensions virtuelles de la dimension Time qui contient une hiérarchie par défaut dont les descendants ordonnés sont : Année,Quarter,Mois,Date:

Voici donc le rendu dans SchemaWorkBench :

Dans cette image , on remarque que les dimensions TimeStart et TimeEnd sont des Dimensions « Usage » c’est à dire qu’elles ont été crées à partir d’une véritable dimension.

Premier essai MDX: la requête

maintenant que l’on a créé le cube, on peut concevoir la requête:

On doit faire un croisement entre

  1. la période qui va de janvier à l’infini dans la dimension TimeStart pour la date de commande ( rappelez-vous la condition SQL : datecommande >= « 01/01/2011 »)
  2. et la période qui va du début de l’existence de l’informatique (merci Turing! – je laisse Snail préciser les choses) à janvier dans la dimension TimeEnd pour la date de devis (condition SQL: datedevis <= "31/01/2011")
  3. et enfin le montant
Bon alors ça donne quoi ?
déjà comment savoir la date du début de l’informatique ? On va utiliser le membre Firstchid de la dimension TimeStart auquel on va adjoindre la fonction MDX Openingperiod.
Du coup la condition 1 se résume à

OpeningPeriod([TimeStart].[Month],[TimeStart].firstchild):[TimeStart].[2011].[Q1].[January]

De même, la condition 2 correspond à ça ( grâce à la fonction ClosingPeriod et au membre Lastchild):

[TimeEnd].[2011].[Q1].[January]:Closingperiod([TimeEnd].[Month],[TimeEnd].lastchild )

Une petite explication s’impose sur ces deux ensembles:
[TimeStart].Firstchild permet d’identifier le premier enfant de cette dimension c’est à dire la première année possible, LastChild étant la dernière année possible.
OpeningPeriod permet de connaitre le dernier enfant possible à un niveau spécifié( [TimeEnd].[Month]) pour un membre spécifique( [TimeEnd].lastchild).
Pour moi, la fin du monde étant positionné à décembre 2013, la formule correspondra à
[TimeEnd].[Q4].[December].
Je vous laisse imaginer l’explication pour ClosingPeriod qui est la contraposée parfaite de la première.
Il suffit maintenant de mettre tout ça dans un bocal, rajouter la mesure Montant et d’agiter très fort. Voilà ce que ça devrait donner :
with MEMBER Measures.x as ‘
SUM( NonEmptyCrossJoin(
OpeningPeriod([TimeStart].[Month],[TimeStart].firstchild):[TimeStart].[2011].[Q1].[January],
[TimeEnd].[2011].[Q1].[January]:Closingperiod([TimeEnd].[Month],[TimeEnd].lastchild ) )
, [Measures].[Montant] )
Select Measures.x on 0
from [Pipe]
Outre le fait que j’utilise un membre calculé, je fais appel à la fonction NonEmptyCrossJoin
qui permet de faire la jointure entre les parcours des deux dimensions ( en enlevant bien sûr les tuples vides) ainsi que la fonction SUM qui permet de faire la somme du montant sur le dataset créé par le crossjoin.
Premier essai MDX: Conclusions
Ok, on a le bon montant du pipe commercial en janvier 2011, mais peut avoir la même chose pour toutes les autres périodes à partir d’une seule requête ?
ça voudrait dire que l’on puisse décrire mois par mois ce qui revient à dire qu’il faut rajouter un axe supplémentaire dans la requête pour montrer les montants pour chaque mois.
Dans bien des cas, cela se résume à rajouter un axe colonne comme ceci :
select Measures.test on rows,
Descendants([Time]. [year],(Month) on columns
from [MonCube]
Fastoche ? Pas vraiment : je vous rappelle que l’on a deux dimensions de temps différentes TimeStart et TimeEnd et que l’on ne peut pas les aligner si facilement…
Comment ? parlez plus fort…
?rajouter une troisième dimension de temps ?
Ok, mais ça suffit pas…. il faut aussi lier les trois dimensions entre elles pour que lorsque l’on se positionne par exemple sur janvier , les deux autres prennent en compte cette période.
Heureusement M$ ( l’inventeur de MDX; respect à Mosha Pasumansky) a pensé à tout : la fonction LinkMember .


Arghh !! Mauvaise Limonade: Julian Hyde, le créateur de Mondrian, ne l’a pas implémenté… voir le ticket si ça dit à quelqu’un(e)..


Dans la troisième partie de cette série on verra comment résoudre le problème:
afficher à partir d’une seule requête MDX le montant du pipe commercial Mois par Mois

Pentaho:calcul du pipe commercial en MDX avec Mondrian – partie 1

Voilà donc un cas d’école de la vraie vie comme un consultant décisionnel aime à en voir: Comment calculer mois par mois un pipe commercial ?

Bon, le besoin est simple: une entreprise veut connaître mois par mois le montant global des propositions commerciales qui ont été envoyées à ses prospects.

En général l’entreprise a un système de force de ventes qui permet d’extraire ces données là.

Par exemple , un extract serait :

N° de devis, N° de client/prospect, date du devis , date d’expiration du devis (ou date de commande),Montant du devis, etc.

Imaginons que nous voulions savoir la liste de tous les devis actifs sur le mois de janvier.

On va donc devoir identifier les cas possibles; ça se résume à cette image :

La solution SQL:

Bon là c’est facile , il suffit de retranscrire les 4 cas possibles dans les conditions de filtre:

select sum(montant) from Pipe where (datedevis>= ’01/01/2011′ and datecommande<=’31/01/2011′ or () etc…

Fastidieux, non ?…

Heureusement, Il y a une façon plus élégante de regrouper les filtres:

select sum(montant) from Pipe  where (datedevis <= ’31/01/2011′) and (datecommande >= ’01/01/2011′ );

Dans le prochain article j’aborderais la solution MDX pour Mondrian.