Support Formation DAX

Fonctions définies par l'utilisateur (UDF)

Les fonctions définies par l'utilisateur (UDF, user-defined functions) permettent d'écrire une logique DAX une seule fois, avec des paramètres, puis de l'appeler comme une fonction native dans les mesures, les colonnes calculées, les calculs visuels et d'autres UDF.

En bref

  • Nouveau mot-clé : FUNCTION, suivi de paramètres typés et d'un corps après =>.
  • Disponibilité : fonctionnalité généralement disponible dans Power BI Desktop et le service Power BI depuis la version de juin 2026 (niveau de compatibilité 1702 ou supérieur). Elle était en préversion depuis septembre 2025.
  • Objet du modèle : une UDF est enregistrée dans le modèle sémantique, comme une mesure, et visible dans l'Explorateur de modèles sous le nœud Functions.
  • Intérêt : centraliser les règles métier, réduire le code dupliqué, faciliter la maintenance.

Où créer une UDF

EmplacementCommentEnregistrer dans le modèle
Vue de requête DAXBloc DEFINE FUNCTION ... suivi d'un EVALUATE pour testerMettre à jour le modèle avec les modifications, ou le lien Mettre à jour le modèle : ajouter une nouvelle fonction au-dessus de la fonction
Vue TMDLScript createOrReplace function ...Bouton Appliquer
Explorateur de modèlesNœud Functions, modification dans la barre de formuleValidation de la formule
Projet Power BI (PBIP)Fichier functions.tmdl du dossier definitionOuverture du projet dans Power BI Desktop

Astuce : dans l'Explorateur de modèles, clic droit sur une fonction > Requête rapide > Définir et évaluer pour la tester immédiatement.

Syntaxe

DEFINE
    /// Description affichée dans IntelliSense
    /// @param {NUMERIC} montant - Montant hors taxe
    /// @returns Montant toutes taxes comprises
    FUNCTION AjouterTaxe =
        ( montant : NUMERIC, taux : NUMERIC = 0.2 ) =>
            montant * ( 1 + taux )

EVALUATE
{ AjouterTaxe ( 100 ) }
// Renvoie 120
  • /// documente la fonction (les commentaires // et /* */ ne s'affichent pas dans IntelliSense).
  • Chaque paramètre peut recevoir des indications de type : nom : [type] [sous-type] [mode].
  • = <expression> rend un paramètre facultatif (valeur par défaut).
  • De 0 à 256 paramètres.

Règles de nommage

  • Nom unique dans le modèle, sans espace ni caractère spécial (lettres, chiffres, soulignés).
  • Points autorisés pour créer des espaces de noms : Ventes.Marge, Finance.AjouterTaxe.
  • Pas de conflit avec une fonction DAX native ou un mot réservé (MEASURE, FUNCTION, DEFINE...).
  • Convention conseillée : PascalCase (CalculerMarge) pour distinguer les UDF des fonctions natives en majuscules.

Types de paramètres

FamilleTypeAccepteÉvaluation
ValeurANYVAL (défaut)Scalaire ou tableImmédiate (VAL)
ValeurSCALARValeur scalaire, avec sous-type facultatifVAL ou EXPR
ValeurTABLETableVAL ou EXPR
ExpressionANYREFToute référenceDifférée (EXPR)
ExpressionCOLUMNREF, MEASUREREF, TABLEREF, CALENDARREFRéférence à une colonne, une mesure, une table ou un calendrierDifférée (EXPR)

Sous-types de SCALAR : VARIANT, INT64, DECIMAL, DOUBLE, STRING, DATETIME, BOOLEAN, NUMERIC. Indiquer un sous-type suffit : x : INT64 équivaut à x : SCALAR INT64.

Modes de passage : VAL ou EXPR

  • VAL (évaluation immédiate) : l'argument est calculé une seule fois, dans le contexte de l'appelant (contexte de ligne et de filtre), puis sa valeur est transmise. C'est le mode par défaut.
  • EXPR (évaluation différée) : l'expression est transmise non évaluée ; la fonction décide quand et dans quel contexte la calculer, par exemple à l'intérieur d'un CALCULATE. Elle n'hérite que du contexte de filtre.

Exemple : les deux fonctions comptent les ventes en supprimant le filtre sur les dates, mais seule la version EXPR y parvient.

DEFINE
    FUNCTION CompterMaintenant = ( t : TABLE VAL ) =>
        COUNTROWS ( CALCULATETABLE ( t, ALL ( 'Date' ) ) )

    FUNCTION CompterPlusTard = ( t : TABLE EXPR ) =>
        COUNTROWS ( CALCULATETABLE ( t, ALL ( 'Date' ) ) )

EVALUATE
{
    CALCULATE ( CompterMaintenant ( 'Ventes' ), 'Date'[Année] = 2020 ),
    CALCULATE ( CompterPlusTard ( 'Ventes' ), 'Date'[Année] = 2020 )
}
// La 1re valeur reste filtrée sur 2020, la 2e porte sur toutes les années

Règle pratique : VAL pour des valeurs simples (montant, taux) ; EXPR dès que la fonction doit modifier le contexte de filtre de l'argument (mesure passée à CALCULATE, table filtrée).

Utiliser une UDF

  • Mesure : Ventes TTC = AjouterTaxe ( [Total Ventes] )
  • Colonne calculée : Montant TTC = CONVERT ( AjouterTaxe ( Ventes[Montant] ), CURRENCY ). Convertir le résultat pour garantir un type cohérent.
  • Calcul visuel : TTC = AjouterTaxe ( [Montant] ). Seuls les champs présents dans le visuel peuvent être passés en argument.
  • Autre UDF : une fonction peut en appeler une autre (mais pas elle-même).

Exemples avec et sans UDF

Taux de croissance

Sans UDF, la même logique est recopiée dans chaque mesure :

Croissance Ventes =
VAR Actuel = [Total Ventes]
VAR Precedent = CALCULATE ( [Total Ventes], SAMEPERIODLASTYEAR ( 'Date'[Date] ) )
RETURN DIVIDE ( Actuel - Precedent, Precedent )

Avec UDF, la règle est écrite une fois :

FUNCTION CroissanceN1 = ( mesure : SCALAR EXPR ) =>
    VAR Actuel = mesure
    VAR Precedent = CALCULATE ( mesure, SAMEPERIODLASTYEAR ( 'Date'[Date] ) )
    RETURN DIVIDE ( Actuel - Precedent, Precedent )

Croissance Ventes = CroissanceN1 ( [Total Ventes] )
Croissance Marge = CroissanceN1 ( [Marge] )

Le mode EXPR est indispensable ici : la mesure doit être recalculée dans le contexte de l'année précédente.

Vérifier le type d'un argument

FUNCTION LongueurTexte = ( s ) =>
    IF ( ISSTRING ( s ), LEN ( s ), BLANK () )

Fonctions de vérification utiles dans le corps d'une UDF : ISNUMERIC, ISINT64, ISDECIMAL, ISDOUBLE, ISSTRING, ISBOOLEAN, ISDATETIME. Fonctions d'information : TABLEOF (table associée à une référence) et NAMEOF (nom d'un objet).

Inspecter les UDF du modèle

EVALUATE INFO.USERDEFINEDFUNCTIONS ()

Renvoie toutes les métadonnées des UDF (droit d'écriture nécessaire). INFO.FUNCTIONS ( "ORIGIN", "2" ) liste seulement leurs noms.

Bonnes pratiques

  • Documenter chaque fonction avec ///, @param et @returns.
  • Typer les paramètres pour sécuriser les appels et rendre le code lisible.
  • Choisir EXPR uniquement quand la fonction doit changer le contexte de l'argument.
  • Regrouper les fonctions par espace de noms (Temps., Finance.).
  • Tester chaque fonction avec EVALUATE dans la vue de requête DAX avant de l'enregistrer.
  • Réutiliser des bibliothèques existantes plutôt que tout réécrire.

Sources : SQLBI | DAX Lib

Limites à connaître

  • Pas de récursivité, pas de surcharge, pas de type de retour explicite.
  • Impossible de masquer une UDF, de la ranger dans un dossier d'affichage ou de la traduire.
  • La sécurité au niveau des objets (OLS) ne se transmet pas aux fonctions : éviter de révéler des objets sécurisés dans leurs noms et descriptions.
  • Pas d'UDF dans un modèle sans table.
  • Pas d'IntelliSense pour les UDF dans la barre de formule des calculs visuels, ni pour les mesures de rapport en connexion active.
  • Un paramètre EXPR qui n'est jamais utilisé dans le corps n'est pas évalué.

Ressources