Des conseils concrets, des bonnes pratiques et des petits détails qui font la différence.
13 astuces2–5 min par sujet100 % pratique
01
EXPRESSION
NULL : pourquoi Sum(A + B) peut vous piéger
Une addition ligne à ligne contenant NULL peut faire disparaître la ligne du calcul. RangeSum() permet souvent d’exprimer plus clairement l’intention.
Ce qui se passe
Qlik évalue d’abord l’expression ligne par ligne. Si une composante de l’addition vaut NULL, le résultat de cette ligne peut devenir NULL avant même que Sum() n’agrège les résultats. C’est donc l’endroit où l’addition est effectuée qui change le résultat.
LE PIÈGE
Supposons trois lignes. Sur la deuxième, MARGE est NULL. L’expression CA_HT + MARGE est alors NULL pour cette ligne : Sum() ne reçoit donc aucune valeur numérique pour cette addition.
À surveiller
Sum(CA_HT + MARGE)
Écriture conseillée
Sum(CA_HT) + Sum(MARGE)
// ou, pour une logique ligne à ligne :
Sum(RangeSum(CA_HT, MARGE))
À RETENIR
RangeSum() traite les valeurs non numériques, dont NULL, comme 0 dans le calcul de la plage. Le choix entre les deux écritures dépend de ce que vous voulez réellement calculer.
02
SCRIPT
Exists() et Not Exists() : le piège du chargement en cours
Exists() ne regarde pas seulement une table précédente : il travaille sur les valeurs déjà présentes dans le symbole du champ, y compris celles chargées pendant le LOAD courant.
Le bon réflexe
Imaginez Exists() comme une question posée au dictionnaire des valeurs déjà connues par Qlik : « cette valeur a-t-elle déjà été rencontrée ? ». Pendant un LOAD, ce dictionnaire évolue au fur et à mesure des lignes chargées.
LE PIÈGE
Avec Not Exists(Client), la première occurrence d’un client peut être chargée puis rendre les occurrences suivantes non admissibles. Ce comportement est parfois voulu, parfois très surprenant.
À surveiller
Clients:
LOAD *
FROM [lib://Data/clients.qvd] (qvd)
WHERE NOT Exists(Client);
Écriture conseillée
ClientsDejaCharges:
LOAD DISTINCT
Client AS Client_Existant
FROM [lib://Data/clients_reference.qvd] (qvd);
NouveauxClients:
LOAD *
FROM [lib://Data/clients.qvd] (qvd)
WHERE NOT Exists(Client_Existant, Client);
À RETENIR
Utiliser un champ de contrôle distinct rend l’intention explicite et évite de confondre dédoublonnage implicite et filtrage métier.
03
MODÈLE
ApplyMap() : éviter un JOIN juste pour récupérer une valeur
Pour enrichir une table avec une correspondance simple clé → valeur, une Mapping Table est souvent plus lisible qu’un JOIN.
Quand l’utiliser
Si chaque CodeProduit doit simplement récupérer un libellé unique, vous avez une correspondance clé → valeur : c’est exactement le terrain d’ApplyMap(). Si une clé peut ramener plusieurs lignes, ce n’est plus un simple mapping.
LE PIÈGE
Un JOIN modifie la structure de la table et peut multiplier les lignes si la clé n’est pas réellement unique. Pour une simple traduction de code, c’est souvent disproportionné.
À surveiller
LEFT JOIN (Ventes)
LOAD
CodeProduit,
LibelleProduit
RESIDENT Produits;
Une Mapping Table attend une correspondance clé → valeur. Si votre besoin est relationnel ou plusieurs-à-plusieurs, un JOIN ou le modèle associatif peut être plus approprié.
04
SCRIPT
Peek() ou Previous() ? Deux fonctions qui ne regardent pas au même endroit
Previous() lit l’enregistrement d’entrée précédent ; Peek() lit une valeur déjà présente dans une table Qlik.
Image mentale
Previous() regarde en arrière dans le flux qui entre dans le LOAD. Peek() regarde dans une table Qlik déjà construite. Avec un WHERE ou un ORDER BY, ces deux « lignes précédentes » peuvent donc ne plus être les mêmes.
LE PIÈGE
La nuance devient importante dès qu’un WHERE, une transformation ou un tri intervient dans le chargement.
À surveiller
// Les deux fonctions ne sont pas interchangeables.
LOAD
Date,
Montant,
Previous(Montant) AS Montant_Precedent
RESIDENT Ventes;
Écriture conseillée
VentesTriees:
LOAD
Date,
Montant,
Montant - Peek('Montant') AS Ecart
RESIDENT Ventes
ORDER BY Date;
À RETENIR
Peek() est particulièrement pratique pour travailler sur le résultat déjà construit. Previous() permet de remonter dans le flux d’entrée du LOAD.
05
SCRIPT
NoConcatenate : empêcher Qlik de fusionner deux tables sans vous prévenir
Deux tables ayant exactement la même structure de champs peuvent être concaténées automatiquement par Qlik.
Pourquoi Qlik le fait
L’auto-concaténation est volontaire : lorsque deux tables possèdent exactement les mêmes champs, Qlik considère souvent qu’elles décrivent le même type de données. NoConcatenate lui indique explicitement que vous voulez malgré tout deux tables distinctes.
LE PIÈGE
Vous pensiez créer une table temporaire séparée, mais elle disparaît comme table autonome parce que ses lignes ont été ajoutées à une table existante.
NoConcatenate doit être utilisé quand la séparation des deux tables est intentionnelle. Ne l’ajoutez pas systématiquement : l’auto-concaténation est aussi une fonctionnalité très utile.
06
DÉBOGAGE
TRACE : votre meilleur ami pour comprendre un script
TRACE écrit un message dans le journal de rechargement. C’est simple, mais redoutablement efficace pour contrôler variables et étapes.
Méthode de diagnostic
Placez un TRACE avant l’étape suspecte, affichez les variables qui pilotent le traitement, puis un autre après. Dans le journal, vous voyez immédiatement jusqu’où le script est allé et avec quelles valeurs.
LE PIÈGE
Un filtre dynamique ne retourne rien et vous ne savez pas quelle valeur contient réellement votre variable au moment du SQL.
À surveiller
LET vAnnee = Year(Today());
SQL SELECT *
FROM COMMANDES
WHERE ANNEE = $(vAnnee);
Écriture conseillée
LET vAnnee = Year(Today());
TRACE === Début chargement COMMANDES ===;
TRACE vAnnee = $(vAnnee);
SQL SELECT *
FROM COMMANDES
WHERE ANNEE = $(vAnnee);
TRACE === Fin chargement COMMANDES ===;
À RETENIR
Pour un diagnostic propre, préfixez vos traces avec un repère constant. Elles deviennent immédiatement visibles dans un log volumineux.
07
SCRIPT
Must_Include : échouer franchement plutôt que continuer avec un script incomplet
Include et Must_Include chargent du script externe, mais Must_Include provoque une erreur si le fichier attendu est inaccessible.
Pourquoi c’est plus sûr
Si Variables.qvs définit des chemins, connexions ou paramètres indispensables, poursuivre sans ce fichier rend la suite du script imprévisible. Must_Include transforme cette dépendance en contrat explicite : absent = rechargement en erreur.
LE PIÈGE
Un fichier contient des variables ou des SUB indispensables. Continuer le rechargement sans lui peut produire un résultat incohérent.
À surveiller
$(Include=lib://Includes/Variables.qvs);
Écriture conseillée
$(Must_Include=lib://Includes/Variables.qvs);
À RETENIR
Pour une dépendance obligatoire, l’échec explicite est généralement préférable. Include reste pertinent lorsqu’un fichier est réellement facultatif.
08
MODÈLE
Créer une clé composée propre avec AutoNumberHash128()
Quand l’unicité dépend de plusieurs champs, une clé technique évite de propager partout une longue concaténation.
Exemple concret
Une facture peut être identifiée par Société + Année + Numéro. Ces trois champs forment la clé métier ; AutoNumberHash128() permet d’en produire une clé technique compacte pour les associations, sans bricoler une chaîne ambiguë.
LE PIÈGE
Concaténer des valeurs sans séparateur fiable peut créer des collisions logiques : 12 & 34 et 1 & 234 donnent tous deux 1234.
À surveiller
LOAD
Societe & Annee & Numero AS %CleFacture,
*
RESIDENT Factures;
Écriture conseillée
LOAD
AutoNumberHash128(
Societe,
Annee,
Numero
) AS %CleFacture,
*
RESIDENT Factures;
À RETENIR
Une clé technique sert au modèle. Conservez séparément les champs métier nécessaires à l’affichage, au contrôle et à l’analyse.
09
EXPRESSION
Dual() : afficher « Septembre » tout en triant comme le mois 9
Dual() associe une représentation texte à une valeur numérique. Parfait pour les mois, statuts ordonnés et libellés métier.
Deux valeurs en une
Dual() conserve simultanément un texte visible et une valeur numérique. L’utilisateur voit « Septembre », tandis que Qlik peut utiliser 9 pour le tri. On sépare ainsi présentation et ordre logique.
LE PIÈGE
Un tri alphabétique des mois donne Avril, Août, Décembre… au lieu de l’ordre chronologique.
À surveiller
Month(DateCommande) AS Mois
Écriture conseillée
Dual(
Month(DateCommande),
Month(DateCommande)
) AS Mois
// Variante explicite :
Dual(
Date(DateCommande, 'MMMM'),
Month(DateCommande)
) AS Mois
À RETENIR
Le principe à retenir est texte pour l’affichage, nombre pour l’ordre. Selon vos paramètres régionaux et votre modèle calendrier, adaptez le libellé.
10
SCRIPT
Resident : transformer ce que Qlik a déjà chargé
Un Resident LOAD permet de retravailler une table déjà en mémoire sans retourner vers la source.
Le chemin des données
La source a déjà été chargée dans la table Ventes. Un Resident LOAD repart de cette table en mémoire pour fabriquer CA_Clients : aucune nouvelle requête vers la base n’est nécessaire pour cette transformation.
LE PIÈGE
Après avoir chargé une source coûteuse, refaire une requête simplement pour créer une agrégation ou une transformation ajoute un accès inutile à la source.
À surveiller
SQL SELECT
Client,
SUM(Montant) AS CA
FROM VENTES
GROUP BY Client;
Écriture conseillée
CA_Clients:
LOAD
Client,
Sum(Montant) AS CA
RESIDENT Ventes
GROUP BY Client;
À RETENIR
Resident n’est pas automatiquement “plus rapide” dans tous les cas. Il est surtout pertinent lorsque les données nécessaires sont déjà présentes dans Qlik.
11
SET ANALYSIS
{1} : ignorer les sélections… mais pas la logique de votre expression
L’identificateur 1 part de l’ensemble de tous les enregistrements de l’application. Cela ne transforme pas pour autant n’importe quelle expression en calcul universel.
Raisonnement
Le Set Analysis se lit en deux temps : choisissez d’abord l’ensemble de départ, ici 1 = tous les enregistrements ; appliquez ensuite les éventuels modificateurs. {1<Annee={2026}>} signifie donc « partir de tout, puis ne garder que 2026 ».
LE PIÈGE
On résume parfois {1} par “sans filtre”. C’est pratique, mais trop simplificateur pour raisonner correctement sur des expressions plus complexes.
Sum({1} Montant)
// On part de l'ensemble 1 puis on peut encore
// le modifier explicitement :
Sum({1<Annee={2026}>} Montant)
À RETENIR
Raisonnez toujours en termes d’ensemble de départ + modificateur. C’est beaucoup plus robuste dès que vos Set Analysis deviennent complexes.
12
EXPRESSION
Sécuriser un ratio quand le dénominateur vaut zéro
Un KPI doit expliciter ce qu’il fait lorsque le dénominateur est nul plutôt que laisser le cas limite implicite.
Pourquoi NULL plutôt que 0
Si CA_HT vaut zéro, le ratio de marge n’est pas calculable. Retourner 0 % affirmerait au contraire qu’un ratio valide a été calculé et vaut zéro. NULL permet de conserver cette différence métier.
LE PIÈGE
Une marge %, un taux de transformation ou un prix moyen peut rencontrer un dénominateur nul selon les sélections.
Retourner NULL permet souvent de distinguer “non calculable” de 0 %, qui est une vraie valeur métier. Le comportement attendu reste à définir avec le métier.
13
SCRIPT
RecNo() ou RowNo() ? Entrée et sortie ne racontent pas la même chose
Les deux fonctions numérotent des lignes, mais pas au même endroit du chargement : RecNo() regarde les enregistrements lus, RowNo() les lignes produites.
La différence essentielle
RecNo() correspond à la position de l’enregistrement dans la source en cours de lecture. RowNo() correspond à la position de la ligne dans la table de sortie. Si toutes les lignes passent, les deux peuvent sembler identiques ; un filtre suffit à faire apparaître leur différence.
LE PIÈGE
Avec un WHERE, certaines lignes de la source sont lues puis rejetées. RecNo() peut alors présenter des trous tandis que RowNo() continue à numéroter les lignes réellement chargées.
À surveiller
// Ne pas supposer que les deux numéros seront toujours identiques.
LOAD
ID_COMMANDE,
RecNo() AS NumeroSource,
RowNo() AS NumeroSortie
RESIDENT CommandesSource
WHERE Montant > 0;
Écriture conseillée
// Choisir selon le besoin :
// position dans la source lue
RecNo() AS NumeroSource
// position dans le résultat chargé
RowNo() AS NumeroSortie
À RETENIR
RecNo() répond à « quelle ligne suis-je en train de lire ? ». RowNo() répond à « quelle ligne suis-je en train de produire ? ». Avec filtres ou concaténations, cette distinction devient visible.