· Par Équipe HeyData

Série : Fonctions et mots-clés méconnus de Qlik Sense : une série · article 5 sur 13

Window() dans Qlik Sense : moyennes, cumuls et fenêtres glissantes dans le script

Calculer la moyenne d’un groupe sans perdre le détail, une moyenne mobile sur trois mois ou un cumul par client : en SQL, ce sont des fonctions de fenêtrage (OVER (PARTITION BY … ORDER BY …)). Le script Qlik a longtemps demandé un détour, GROUP BY puis jointure, ou ORDER BY puis Peek(). La fonction Window() fait le travail en une ligne.

Cet article fait partie de notre série sur les fonctions et mots-clés méconnus de Qlik Sense. Il s’appuie sur l’aide Qlik ; les liens sont en fin d’article.

GROUP BY réduit, Window conserve

Un LOAD … GROUP BY Region rend une ligne par région : le détail disparaît. Window() calcule la même agrégation, mais la renvoie sur chaque ligne. Le nombre de lignes ne change pas.

Deux tables côte à côte. À gauche, le résultat d’un LOAD avec GROUP BY Region : deux lignes, Nord 115 et Sud 80, le détail par mois est perdu. À droite, la table Sales avec Window(Avg(Sales), Region) : huit lignes, quatre mois pour Nord et quatre pour Sud, chacune garde ses ventes et reçoit la moyenne de sa région, 115 pour les lignes Nord surlignées et 80 pour les lignes Sud.

Même moyenne, mais Window garde les huit lignes.

Sales:
LOAD *,
     Window(Avg(Sales), Region) as AvgRegion
INLINE [
Region, Month, Sales
Nord, 1, 100
Nord, 2, 120
Sud, 1, 80
Sud, 2, 60
];

La syntaxe

Window(main_expr [, partition1, partition2, …] [, sort_type, sort_expr] [, filter_expr] [, start_expr, end_expr])
  • main_expr : l’agrégation à calculer, par exemple Avg(Sales). Un champ seul est traité comme Only(champ). On ne peut pas y imbriquer une autre agrégation ni un autre Window().
  • Les partitions : les champs qui découpent la table en groupes. Sans partition, toute la table forme un seul groupe.
  • sort_type : 'ASC', 'DESC' ou 'NONE'. Les deux premiers exigent une expression de tri sort_expr, le troisième n’en prend pas.
  • filter_expr : une condition. Les lignes qui ne la vérifient pas sont exclues du calcul, mais reçoivent quand même un résultat. Le filtre demande un sort_type, 'NONE' si l’ordre est indifférent.
  • start_expr, end_expr : les bornes d’une fenêtre glissante, en nombre de lignes par rapport à la ligne courante. Elles vont toujours par deux, et demandent un tri.

Le tri se règle dans la fonction elle-même : nul besoin d’un ORDER BY sur le LOAD.

Fenêtre glissante et cumul

Avec un tri et deux bornes, la fenêtre suit la ligne courante. -2, 0 prend les deux lignes précédentes et la ligne courante : c’est une moyenne mobile sur trois mois. Unbounded() remplace une borne par « toutes les lignes jusqu’au bord de la partition » : avec Unbounded(), 0, on obtient un cumul.

La table Sales filtrée sur la région Nord, avec quatre mois. Pour le mois 3, les lignes des mois 1 à 3 sont surlignées et un crochet indique la fenêtre de -2 à 0. La colonne Moyenne mobile vaut 100, 110, 103,3 et 120 ; la colonne Cumul vaut 100, 220, 310 et 460. En dessous, les deux expressions Window correspondantes.

La moyenne mobile du mois 3 porte sur les mois 1 à 3.

Sales:
LOAD *,
     Window(Avg(Sales), Region, 'ASC', Month, -2, 0)          as MovingAvg3,
     Window(Sum(Sales), Region, 'ASC', Month, Unbounded(), 0) as RunningTotal
INLINE [
Region, Month, Sales
Nord, 1, 100
Nord, 2, 120
Nord, 3, 90
Nord, 4, 150
];

Au début de la partition, la fenêtre est simplement plus courte : la moyenne mobile du mois 1 ne porte que sur le mois 1. Si vous voulez un résultat vide tant que la fenêtre n’est pas complète, testez le nombre de lignes avec Window(Count(Sales), Region, 'ASC', Month, -2, 0) = 3.

Le tri compte. Un mois stocké en texte, comme 2026-S01 ou Jan 2026, se trie dans l’ordre alphabétique. La Qlik Community rapporte régulièrement des fenêtres faussées pour cette raison : triez sur une valeur numérique, une date ou un numéro de période.

Filtrer sans perdre de lignes

Le filtre exclut des lignes du calcul sans les retirer de la table :

Window(Avg(Sales), Region, 'NONE', Month >= 2) as AvgFromMonth2

Pour Nord, la moyenne porte sur les mois 2 à 4 (120, 90 et 150, soit 120). Le mois 1 reçoit pourtant cette valeur lui aussi. C’est utile pour comparer chaque ligne à une référence calculée sur une partie seulement des données.

Avant Window : la méthode classique

La même moyenne par région s’écrivait avec une agrégation puis une jointure :

Left Join (Sales)
LOAD Region, Avg(Sales) as AvgRegion
RESIDENT Sales
GROUP BY Region;

Pour une fenêtre glissante ou un cumul, il fallait trier avec ORDER BY et remonter les lignes avec Peek(), comme dans notre article sur la génération des données manquantes. Ces méthodes restent valables, et souvent plus économes : Window() n’est pas gratuite.

Les limites

L’aide Qlik les liste clairement :

  • Un coût élevé. Comparée à une opération équivalente (WHERE, GROUP BY, ORDER BY), une fonction Window() demande en général deux à quatre fois plus de mémoire et de temps de rechargement. Avec une fenêtre glissante, le temps croît avec la taille de la fenêtre.
  • Le script uniquement. Window() n’existe pas dans les expressions de graphique. Côté visualisation, les fonctions inter-lignes (Above(), RangeSum()…) et Aggr() jouent ce rôle.
  • Pas de combinaison. Pas de GROUP BY dans le même LOAD, pas d’imbrication dans une agrégation ni dans un autre Window().
  • Des fonctions exclues. RecNo(), RowNo() et WRank() ne sont pas utilisables avec une fenêtre glissante.
  • Toute la table. La fonction doit pouvoir parcourir toute la table, et elle n’est pas prise en charge par Qlik Sense Mobile.

Côté versions, Window() est arrivée sur Qlik Sense Enterprise on Windows en novembre 2023. À cette sortie, elle devait être activée dans le fichier Settings.ini du moteur (EnableWindowScriptFunction=1). Vérifiez la documentation de votre version avant de l’utiliser en client-managed. Elle est disponible dans Qlik Cloud.

À retenir

  • Une valeur par ligne. Window calcule par partition sans réduire la table, contrairement à GROUP BY.
  • Le tri est dans la fonction. 'ASC' ou 'DESC' avec une expression de tri, 'NONE' sinon : pas d’ORDER BY à ajouter.
  • Deux bornes pour glisser. -2, 0 pour une moyenne mobile, Unbounded(), 0 pour un cumul.
  • Un filtre qui ne supprime rien. Les lignes filtrées sortent du calcul, pas de la table.
  • Triez sur un nombre. Un mois en texte fausse l’ordre de la fenêtre.
  • Mesurez le coût. Deux à quatre fois plus de mémoire et de temps : sur une grosse table, comparez avec GROUP BY ou Peek().

Essayer dans Qlik Sense

Ce script reprend les exemples de l’article avec les données des schémas, chargées en INLINE : il ne dépend d’aucun fichier ni d’aucune base. Collez-le dans l’éditeur de chargement de données d’une application vide, choisissez l’exemple avec la variable vExemple, puis rechargez.

// Window() dans Qlik Sense : script d'essai (HeyData)
// Collez-le dans l'éditeur de chargement de données d'une application vide, choisissez
// l'exemple, rechargez, puis affichez la table dans la visionneuse du modèle de données.
SET vExemple = 1;   // 1 à 3

Sales:
LOAD * INLINE [
Region, Month, Sales
Nord, 1, 100
Nord, 2, 120
Nord, 3, 90
Nord, 4, 150
Sud, 1, 80
Sud, 2, 60
Sud, 3, 110
Sud, 4, 70
];

IF $(vExemple) = 1 THEN
    // Exemple 1 : la moyenne par région, sur chaque ligne (premier schéma)
    Result:
    NoConcatenate
    LOAD *,
         Window(Avg(Sales), Region) as AvgRegion
    RESIDENT Sales;
    // 8 lignes ; AvgRegion vaut 115 pour Nord et 80 pour Sud.
END IF

IF $(vExemple) = 2 THEN
    // Exemple 2 : moyenne mobile sur 3 mois et cumul (deuxième schéma)
    Result:
    NoConcatenate
    LOAD *,
         Window(Avg(Sales), Region, 'ASC', Month, -2, 0)          as MovingAvg3,
         Window(Sum(Sales), Region, 'ASC', Month, Unbounded(), 0) as RunningTotal
    RESIDENT Sales;
    // Nord : MovingAvg3 = 100, 110, 103.3, 120 ; RunningTotal = 100, 220, 310, 460.
END IF

IF $(vExemple) = 3 THEN
    // Exemple 3 : une moyenne filtrée, et la méthode classique pour comparer
    Result:
    NoConcatenate
    LOAD *,
         Window(Avg(Sales), Region, 'NONE', Month >= 2) as AvgFromMonth2
    RESIDENT Sales;

    Left Join (Result)
    LOAD Region, Avg(Sales) as AvgRegionGroupBy
    RESIDENT Sales
    GROUP BY Region;
    // AvgFromMonth2 : 120 pour Nord, 80 pour Sud, y compris sur la ligne du mois 1.
END IF

DROP TABLE Sales;

Sources

Le texte et les exemples sont de HeyData. Cet article fait partie de notre série sur les fonctions et mots-clés méconnus de Qlik Sense : découvrir la série.