HAVING en MDX, une clause méconnue et pourtant performante

Le MDX a une clause HAVING. Je sais ça peut paraître une nouveauté pour certains – je ne l’ai pas appris immédiatement loin s’en faut alors qu’elle existe depuis 2005. La seule documentation réellement utilisable sur cette clause est un papier de William Pearson datant de 2007 dans lequel celui-ci détaille une équivalence fonctionnelle entre HAVING et Filter().

Dit autrement, HAVING peut – pour lui – se voir comme un Filter() appliqué sur un Axis, ces deux notations étant alors interchangeables

{
   Filter
   (
      [Dimension].[Level].Members,
      [Measures].[Measure] > 42
   )
} ON 0
{
   [Dimension].[Level].Members
}
HAVING [Measures].[Measure] > 42
ON 0

Si HAVING est effectivement utile pour réaliser des filtrages, elle présente un intérêt évident par rapport à un Filter(). Non qu’elle soit “block-computationable” , ce n’est pas le cas. En revanche elle s’exécute après l’auto-existence des axes et des NON EMPTY. Et ça ça peut être intéressant.

Prenons un exemple bidon: je souhaite afficher la liste des ventes par jour de plus de 10000$. Ecrivons d’abord la requête de base.

SELECT
{
   [Measures].[Internet Sales Amount]
} ON 0
,
NON EMPTY
{
   [Product].[Product].[Product] 
   * 
   [Date].[Date].[Date]
} ON 1
FROM [Adventure Works];

Cette requête MDX, passée au MDX Studio, s’exécute en 200ms et affiche un Cells Calculated de 23797 avec 1 NON EMPTY, et 1 Sonar Subcube. En gros on a appliqué NON EMPTY sur l’Axe, ce qui a renvoyé 23000 cellules, et généré une requête au Storage Engine.

Si on passe l’Axis 1 sous un Filter() pour les fameux 10000$, le Cells Calculated passe à… 869965, et le temps d’exécution explose. Normal, vous connaissez le fonctionnement du Filter: il a itéré sur toutes les combinaisons – produit cartésien oblige – des dates et des produits sans « Exister » avant le Set sur la mesure.

Time              : 2 sec 307 ms
Calc covers       : 0
Cells calculated  : 869965
Sonar subcubes    : 1
NON EMPTYs        : 1

Tout à fait logiquement, en appliquant un NonEmpty() sur le set avant d’appliquer le Filter, on retombe sur 23000 cellules, et on a cette fois ci 2 Sonar Subcubes et 2 NON EMPTY (Celui de la fonction NonEmpty(), et celui du NON EMPTY sur l’axe).

SELECT
{
   [Measures].[Internet Sales Amount]
} ON 0,
NON EMPTY
{
   Filter
   (
      NonEmpty
      (
         [Product].[Product].[Product]
         * [Date].[Date].[Date]
         ,[Measures].[Internet Sales Amount]
      )
      ,[Measures].[Internet Sales Amount]>10000
   )
} ON 1
FROM [Adventure Works]

Time : 135 ms
Calc covers : 0
Cells calculated : 23935
Sonar subcubes : 2
NON EMPTYs : 2

Alors quel est l’intérêt du HAVING? Il vous permet de filtrer l’axe après l’application de l’ordre NON EMPTY sur les Axis, et donc de vous éviter un NonEmpty (Exists) supplémentaire et inutile dans la clause Filter. Pas le truc de l’année, mais si ça permet d’économiser du temps de cerveau disponible…

SELECT
{
   [Measures].[Internet Sales Amount]
} ON 0
,NON EMPTY
{
   [Product].[Product].[Product] 
   * 
   [Date].[Date].[Date]
}
HAVING [Measures].[Internet Sales Amount] > 10000 
ON 1
FROM [Adventure Works];

HAVING peut donc se révéler très utile et rapide pour faire un filtrage (type Filter) post-application des NON EMPTY des axes, dans un rapport RS par exemple, et sans vouloir s’embêter à écrire soi-même la logique d’Exists/NonEmpty spécifique à ceux-ci.

A bientôt!

Laisser un commentaire

Entrez vos coordonnées ci-dessous ou cliquez sur une icône pour vous connecter:

Logo WordPress.com

Vous commentez à l'aide de votre compte WordPress.com. Déconnexion / Changer )

Image Twitter

Vous commentez à l'aide de votre compte Twitter. Déconnexion / Changer )

Photo Facebook

Vous commentez à l'aide de votre compte Facebook. Déconnexion / Changer )

Photo Google+

Vous commentez à l'aide de votre compte Google+. Déconnexion / Changer )

Connexion à %s