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.
Source : Microsoft Learn
Où créer une UDF
| Emplacement | Comment | Enregistrer dans le modèle |
| Vue de requête DAX | Bloc DEFINE FUNCTION ... suivi d'un EVALUATE pour tester | Mettre à 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 TMDL | Script createOrReplace function ... | Bouton Appliquer |
| Explorateur de modèles | Nœud Functions, modification dans la barre de formule | Validation de la formule |
| Projet Power BI (PBIP) | Fichier functions.tmdl du dossier definition | Ouverture 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.
Source : Microsoft Learn
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
| Famille | Type | Accepte | Évaluation |
| Valeur | ANYVAL (défaut) | Scalaire ou table | Immédiate (VAL) |
| Valeur | SCALAR | Valeur scalaire, avec sous-type facultatif | VAL ou EXPR |
| Valeur | TABLE | Table | VAL ou EXPR |
| Expression | ANYREF | Toute référence | Différée (EXPR) |
| Expression | COLUMNREF, MEASUREREF, TABLEREF, CALENDARREF | Référence à une colonne, une mesure, une table ou un calendrier | Diffé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.
Source : Microsoft Learn
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éesRè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
///,@paramet@returns. - Typer les paramètres pour sécuriser les appels et rendre le code lisible.
- Choisir
EXPRuniquement quand la fonction doit changer le contexte de l'argument. - Regrouper les fonctions par espace de noms (
Temps.,Finance.). - Tester chaque fonction avec
EVALUATEdans la vue de requête DAX avant de l'enregistrer. - Réutiliser des bibliothèques existantes plutôt que tout réécrire.
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
EXPRqui n'est jamais utilisé dans le corps n'est pas évalué.
Source : Microsoft Learn
Ressources
- Fonctions DAX définies par l'utilisateur | Microsoft Learn
- Vue de requête DAX | Microsoft Learn
- Vue TMDL | Microsoft Learn
- Introducing user-defined functions in DAX | SQLBI
- DAX Lib | bibliothèque libre de fonctions UDF