Partager via


DAX fonctions définies par l’utilisateur (préversion)

Note

DAX Les fonctions définies par l’utilisateur sont actuellement en préversion.

DAX Les fonctions définies par l’utilisateur (UDF) vous permettent de empaqueter DAX la logique et de la réutiliser comme n’importe quelle autre DAX fonction. Les fonctions définies par l’utilisateur introduisent un nouveau FUNCTION mot clé, des paramètres facultatifs (scalaires, tables et références), et des outils de vérification de type qui rendent la création plus sûre et plus claire. Après avoir défini une fonction UDF, vous pouvez l’utiliser dans une mesure, une colonne calculée, un calcul visuel ou même d’autres fonctions définies par l’utilisateur. Les utilisateurs peuvent centraliser les règles métier, améliorer la facilité de maintenance et faire évoluer les calculs en toute sécurité au fil du temps. Les fonctions sont des objets de modèle de première classe que vous pouvez créer et gérer en DAX mode Requête et TMDL, et ils peuvent être consultés dans l’Explorateur de modèles sous le nœud Functions.

Activer les fonctions définies par l’utilisateur

Pour essayer les fonctions définies par l’utilisateur dans Desktop :

  1. Accédez aux options de fichier > et aux paramètres>.
  2. Sélectionnez Fonctionnalités en préversion et vérifiez les DAX fonctions définies par l’utilisateur.
  3. Sélectionnez OK et redémarrez Power BI Desktop.

Définir et gérer des fonctions définies par l’utilisateur

Il existe plusieurs emplacements pour définir et gérer des fonctions :

  • DAX vue de requête (DQV). Définissez et modifiez des fonctions dans DQV. DQV inclut également des requêtes rapides dans le menu contextuel (Évaluer, Définir et évaluer, et Définir toutes les fonctions dans ce modèle) pour vous aider à tester et gérer rapidement les fonctions utilisateur.
  • Vue TMDL. Les UDF (fonctions définies par l'utilisateur) peuvent également être créées et modifiées dans TMDL. La vue TMDL inclut également l'option de Script TMDL vers dans le menu contextuel.
  • Explorateur de modèles. Les fonctions existantes peuvent être consultées sous le nœud Functions dans l’Explorateur de modèles.

Lors de la définition d’une fonction UDF, suivez les exigences de nommage suivantes :

Noms de fonction :

  • Doit être bien formé et unique au sein du modèle.
  • Peut inclure des points (points) pour l’espacement des noms (par exemple, Microsoft.PowerBI.MyFunc). Impossible de commencer ou de se terminer par une période ou d’avoir des périodes consécutives.
  • À part les points, les noms ne peuvent contenir que des caractères alphanumériques ou des soulignements. Aucun espace ou caractères spéciaux n’est autorisé.
  • Ne doit pas entrer en conflit avec les fonctions intégrées DAX ou les mots réservés (par exemple, mesurer, fonction, définir).

Noms de paramètres :

  • Ne peut contenir que des caractères alphanumériques ou des traits de soulignement. Les périodes ne sont pas autorisées.
  • Ne doit pas être un mot réservé.

Utilisation de la vue de requête DAX

Vous pouvez définir, mettre à jour et évaluer des fonctions définies par l’utilisateur en DAX mode requête. Pour plus d’informations sur la vue DAX des requêtes, consultez la DAX vue des requêtes.

Formulaire général

DEFINE
    /// Optional description above the function
    FUNCTION <FunctionName> = ( [ParameterName]: [ParameterType], ... ) => <FunctionBody>

Conseil / Astuce

Utiliser /// pour les descriptions de fonction. Les commentaires à ligne unique (//) ou multiligne (/* */) ne s’affichent pas dans les descriptions des fonctions IntelliSense.

Exemple : Fonction fiscale simple

DEFINE
    /// AddTax takes in amount and returns amount including tax
    FUNCTION AddTax = 
        ( amount : NUMERIC ) =>
            amount * 1.1

EVALUATE
{ AddTax ( 10 ) }
// Returns 11

Enregistrement dans le modèle

Pour enregistrer une fonction UDF à partir de DAX vue de requête dans le modèle :

  • Cliquez sur Mettre à jour le modèle avec les modifications pour enregistrer toutes les UDF (fonctions définies par l'utilisateur) dans la requête.
  • Ou cliquez sur Mettre à jour le modèle : ajoutez une nouvelle fonction au-dessus de la fonction définie pour enregistrer une seule fonction UDF.

Capture d’écran de l’affichage des DAX requêtes dans Power BI Desktop, mettant en surbrillance deux emplacements où vous pouvez enregistrer une fonction définie par l’utilisateur. Le premier est le modèle de mise à jour avec le bouton Modifier en haut de la vue. La deuxième est une ligne d’état dans l’éditeur de code intitulé Modèle de mise à jour : Ajouter une nouvelle fonction

Utilisation de la vue TMDL

Vous pouvez définir et/ou mettre à jour des fonctions définies par l’utilisateur en mode TMDL. Pour plus d’informations sur l’affichage TMDL, consultez l’affichage TMDL.

Formulaire général

createOrReplace
    /// Optional description above the function
    function <FunctionName> = ( [ParameterName]: [ParameterType], ... ) => <FunctionBody>

Exemple : Fonction fiscale simple

createOrReplace
    /// AddTax takes in amount and returns amount including tax
    function AddTax = 
        (amount : NUMERIC) =>
            amount * 1.1

Enregistrement dans le modèle

Cliquez sur le bouton Appliquer en haut de la vue pour enregistrer toutes les fonctions définies par l’utilisateur dans le script sur le modèle.

Capture d’écran de la vue TMDL dans Power BI Desktop, mettant en surbrillance le bouton Appliquer en haut de la vue. Il s’agit de l’emplacement où vous pouvez enregistrer une fonction définie par l’utilisateur.

Utilisation du script TMDL dans un projet Power BI

Les UDF sont également incluses dans le modèle sémantique TMDL lors de l’utilisation d’un projet Power BI. Elles se trouvent dans functions.tmdl le dossier de définition .

Capture d’écran de Visual Studio Code d’un projet Power BI. L’Explorateur est ouvert au dossier de modèle sémantique. 'functions.tmdl' est ouvert dans l’éditeur de code. Trois fonctions sont affichées : CustomerLifetimeValue, AverageOrderValue et AddTax.

Utilisation de l’Explorateur de modèles

Vous pouvez afficher toutes les fonctions définies par l’utilisateur dans le modèle à partir de l’Explorateur de modèles sous le nœud Functions . Pour plus d’informations sur l’Explorateur de modèles, consultez l’Explorateur de modèles.

Panneau Explorateur de modèles dans Power BI Desktop montrant le nœud Functions développé. Trois fonctions définies par l’utilisateur sont répertoriées : AddTax, AverageOrderValue et CustomerLifetimeValue.

En DAX mode Requête, vous pouvez utiliser des requêtes rapides dans le menu contextuel d’une fonction UDF dans l’Explorateur de modèles pour définir et évaluer facilement les fonctions.

Le volet Explorateur de modèles dans Power BI Desktop affiche le nœud Functions développé. Deux menus contextuels sont ouverts : le premier menu fournit des requêtes rapides, Renommer, Supprimer du modèle, Masquer en mode Rapport, Afficher tout, Réduire tout et Développer tout. Les requêtes rapides sont mises en surbrillance et sélectionnées. Le deuxième menu est mis en surbrillance et offre des options de requêtes rapides Évaluer, Définir et évaluer, Définir une nouvelle fonction et Définir toutes les fonctions dans ce modèle.

En mode TMDL, vous pouvez glisser-déposer des fonctions dans le canevas ou utiliser 'Script TMDL pour' dans le menu contextuel d'une fonction UDF dans l'Explorateur de modèles pour générer des scripts.

Le volet Explorateur de modèles dans Power BI Desktop affiche le nœud Functions développé. Deux menus contextuels sont ouverts : le premier menu fournit Script TMDL to, Renommer, Supprimer du modèle, Masquer en mode Rapport, Afficher tout, Réduire tout et Développer tout. Script TMDL to est mis en surbrillance et sélectionné. Le deuxième menu est mis en surbrillance et propose l’onglet Script TMDL et le Presse-papiers.

Utilisation des DMV pour inspecter les fonctions définies par l’utilisateur

Vous pouvez inspecter les fonctions définies par l’utilisateur dans votre modèle à l’aide de vues de gestion dynamique (DMVs). Ces vues vous permettent d’interroger des informations sur les fonctions, y compris les UDF (fonctions définies par l'utilisateur).

Vous pouvez utiliser la fonction INFO.FUNCTIONS pour inspecter les UDF dans le modèle. Pour limiter le résultat aux fonctions définies par l’utilisateur uniquement, spécifiez le ORIGIN paramètre en tant que 2.

EVALUATE INFO.FUNCTIONS("ORIGIN", "2")

Cette requête retourne une table de toutes les fonctions définies par l'utilisateur (UDFs) actuellement dans le modèle, y compris leur nom, leur description et leurs métadonnées associées.

Utilisation d’une fonction définie par l’utilisateur

Une fois qu’une fonction UDF est définie et enregistrée dans le modèle, vous pouvez l’appeler à partir de mesures, de colonnes calculées, de calculs visuels et d’autres fonctions définies par l’utilisateur. Cela fonctionne de la même façon que lorsqu'on appelle des fonctions intégrées DAX.

Appel d’une fonction définie par l'utilisateur dans une mesure

Utilisez une fonction UDF dans une mesure pour appliquer une logique réutilisable avec un contexte de filtre complet.

Total Sales with Tax = AddTax ( [Total Sales] )

L’exemple de mesure est illustré dans le tableau ci-dessous :

Tableau montrant le Total des ventes et le Total des ventes avec taxe. Le Total des ventes avec taxe est mis en surbrillance. Le volet Visualisations est ouvert. Le Total des ventes avec taxe est mis en surbrillance dans le champ Colonnes.

Appel d’une fonction UDF dans une colonne calculée

Les fonctions définies par l’utilisateur (UDFs) peuvent être utilisées dans une colonne calculée pour appliquer une logique qui peut être réutilisée à chaque ligne d'une table.

Note

Lorsque vous utilisez une fonction UDF dans une colonne calculée, vérifiez que la fonction retourne un scalaire d’un type cohérent. Pour plus d’informations, consultez Paramètres . Si nécessaire, convertissez le résultat en type souhaité à l’aide CONVERT ou à des fonctions similaires.

Sales Amount with Tax = CONVERT ( AddTax ( 'Sales'[Sales Amount] ), CURRENCY )

Nous pouvons voir cet exemple de mesure utilisé dans le tableau ci-dessous :

Tableau montrant le montant des ventes et le montant des ventes avec taxe. Le montant des ventes avec taxe est mis en surbrillance. Le volet Visualisations est ouvert. Le montant des ventes avec taxe est mis en surbrillance dans le champ Colonnes.

Appel d’une fonction UDF dans un calcul visuel

Vous pouvez utiliser des fonctions définies par l’utilisateur dans un calcul visuel pour appliquer la logique directement au visuel. Pour plus d’informations sur les calculs visuels, consultez Calculs visuels.

Note

Les calculs visuels fonctionnent uniquement sur les champs présents dans le visuel. Ils ne peuvent pas accéder aux objets de modèle qui ne font pas partie du visuel, et vous ne pouvez pas passer des objets de modèle (tels que des colonnes ou des mesures non contenues dans le visuel) dans une fonction UDF dans ce contexte.

Sales Amount with Tax = AddTax ( [Sales Amount] )

Nous pouvons voir cet exemple de mesure dans le tableau ci-dessous :

En mode d’édition du calcul visuel. Tableau montrant le montant des ventes et le montant des ventes avec taxe. Le montant des ventes avec taxe est mis en surbrillance. La formule de calcul visuel pour le montant des ventes avec taxe est mise en surbrillance.

Appel d’une UDF dans une autre UDF

Vous pouvez imbriquer des fonctions définies par l’utilisateur en appelant une fonction à partir d’une autre. Dans cet exemple, nous définissons notre UDF simple AddTax et nous l'appelons dans une autre UDF, AddTaxAndDiscount.

DEFINE
    /// AddTax takes in amount and returns amount including tax
    FUNCTION AddTax = 
        ( amount : NUMERIC ) =>
            amount * 1.1

	FUNCTION AddTaxAndDiscount = 
        (
			amount : NUMERIC,
			discount : NUMERIC
		) =>
		    AddTax ( amount - discount )

EVALUATE
{ AddTaxAndDiscount ( 10, 2 ) }
// Returns 8.8

Paramètres

DAX Les fonctions définies par l’utilisateur peuvent accepter zéro ou plusieurs paramètres. Lorsque vous définissez des paramètres pour une fonction UDF, vous pouvez éventuellement spécifier des indicateurs de type pour chaque paramètre :

  • Type : quel type de valeur le paramètre accepte (AnyVal, Scalar, Tableou AnyRef).
  • Sous-type (uniquement pour le type scalaire) : type de données scalaire spécifique (Variant, , Int64, DecimalDouble, String, DateTimeBooleanou Numeric).
  • ParameterMode : lorsque l’argument est évalué (val ou expr).

Les indicateurs de type sont au format : [type] [subtype] [parameterMode]

Vous pouvez inclure tous, certains ou aucun de ces indicateurs de type pour chaque paramètre pour rendre vos fonctions plus sûres et plus prévisibles sur les sites d’appel. Si vous omettez tout et que vous écrivez simplement le nom du paramètre, il se comporte comme AnyVal val, ce qui signifie que l’argument est évalué immédiatement au moment de l’appel. Cela est utile pour les fonctions simples.

Type

Le type définit la catégorie d’argument que votre paramètre accepte et indique s’il est passé en tant que valeur ou expression.

Il existe deux familles de types dans les DAX paramètres UDF : les types valeur et les types d’expression :

  • Types de valeurs : cet argument est évalué immédiatement (évaluation impatiente) lorsque la fonction est appelée et que la valeur résultante est passée à la fonction.
    • AnyVal: accepte un scalaire ou une table. Il s’agit de la valeur par défaut si vous omettez le type d’un paramètre.
    • Scalar: accepte une valeur scalaire (peut également ajouter un sous-type).
    • Table: accepte une table.
  • Types d’expressions : cet argument transmet une expression non évaluée (évaluation différée). La fonction décide quand et dans quel contexte l’évaluer. Cela est nécessaire pour les paramètres de référence et utiles lorsque vous devez contrôler le contexte de filtre (par exemple, à l’intérieur CALCULATE). expr les types peuvent être des références à une colonne, une table, un calendrier ou une mesure.
    • AnyRef: accepte une référence (une colonne, une table, un calendrier ou une mesure).

Sous-type

Le sous-type vous permet de définir un type de données spécifique Scalar . Si vous définissez un sous-type, vous n’avez pas besoin de définir explicitement le paramètre en tant que Scalar type, cela est automatiquement supposé.

Les sous-types sont les suivants :

  • Variant: accepte n’importe quel scalaire.
  • Int64: accepte un nombre entier.
  • Decimal: accepte une décimale de précision fixe (telle que Devise ou Argent).
  • Double: accepte une décimale à virgule flottante.
  • String: accepte le texte.
  • DateTime: accepte la date/heure.
  • Boolean: accepte TRUE/FALSE.
  • Numeric: accepte n’importe quelle valeur numérique (Int64, Decimalou Double sous-types)

ParameterMode

ParameterMode contrôle quand et où l’expression de paramètre est évaluée. Ces règles sont les suivantes :

  • val (évaluation impatiente) : l’expression est évaluée une fois avant d’appeler la fonction. La valeur résultante est ensuite passée dans la fonction. Cela est courant pour les entrées scalaires ou de table simples. Il s’agit de la valeur par défaut si vous omettez parameterMode pour un paramètre.
  • expr (évaluation différée) : l’expression est évaluée à l’intérieur de la fonction, potentiellement dans un contexte différent (par exemple, contexte de ligne ou contexte de filtre) et éventuellement plusieurs fois si elle est référencée plusieurs fois ou à l’intérieur des itérations. Cela est nécessaire pour les paramètres de référence et utiles lorsque vous devez contrôler le contexte d’évaluation.

Le Scalar type peut utiliser soit val ou expr. Utilisez val quand vous souhaitez que le scalaire soit évalué une fois dans le contexte de l’appelant. Utilisez expr quand vous souhaitez différer l’évaluation et éventuellement appliquer le contexte à l’intérieur de la fonction. Voir Exemple : paramètre table comme exemple.

Le type AnyRef doit être expr car ses références (colonnes, tables, mesures, etc.) doivent être évaluées dans le contexte de la fonction.

Exemple : Conversion de type

DEFINE
    /// returns x cast to an Int64
    FUNCTION CastToInt = (
            x : SCALAR INT64 VAL
        ) =>
        x

EVALUATE
{ CastToInt ( 3.4 ), CastToInt ( 3.5 ), CastToInt ( "5" ) }
// returns 3, 4, 5

Cela utilise un type Scalar, un sous-type Int64, et un modeDeParamètre val pour un arrondi prévisible et une conversion de texte en nombre, tout en garantissant que toutes les expressions sont évaluées de manière anticipée. Vous pouvez également y parvenir en incluant simplement le Int64 sous-type, comme indiqué dans l’exemple ci-dessous. Les chaînes non numériques entraînent une erreur.

DEFINE
    /// returns x as an Int64
    FUNCTION CastToInt = (
            x : INT64
        ) =>
        x

EVALUATE
{ CastToInt ( 3.4 ), CastToInt ( 3.5 ), CastToInt ( "5" ) }
// returns 3, 4, 5

Exemple : paramètre de table (valeur et expression)

Pour illustrer la façon dont UDF parameterMode affecte le contexte de filtre, considérez deux fonctions qui comptent les lignes dans la table « Sales ». Les deux utilisent CALCULATETABLE(t, ALL('Date')) dans leur corps, mais un paramètre est déclaré comme une val (évaluation rapide) et l'autre comme expr (évaluation différée) :

DEFINE
    /// Table val: receives a materialized table, context can't be changed
    FUNCTION CountRowsNow = (
            t : TABLE VAL
        ) =>
        COUNTROWS ( CALCULATETABLE ( t, ALL ( 'Date' ) ) )
    
    /// Table expr: receives an unevaluated expression, context CAN be changed
    FUNCTION CountRowsLater = (
            t : TABLE EXPR
        ) =>
        COUNTROWS ( CALCULATETABLE ( t, ALL ( 'Date' ) ) )

EVALUATE
{
    CALCULATE ( CountRowsNow ( 'Sales' ), 'Date'[Fiscal Year] = "FY2020" ),
    CALCULATE ( CountRowsLater ( 'Sales' ), 'Date'[Fiscal Year] = "FY2020" )
}
// returns 84285, 121253

CountRowsNow retourne uniquement le nombre de ventes pour FY2020. La table « Sales » est déjà filtrée par année avant d’entrer dans la fonction, de sorte que ALL('Date') dans la fonction n’a aucun effet.

CountRowsLater renvoie le nombre de ventes pour toutes les années. La fonction reçoit une expression de table non évaluée et l’évalue sous ALL('Date'), en supprimant le filtre d’année externe.

Vérification de type

La vérification des types dans les fonctions définies par l’utilisateur peut être effectuée avec des fonctions de vérification de type nouvelles et existantes que vous pouvez appeler dans le corps de votre fonction pour confirmer le type en cours d'exécution des paramètres transmis. Cela permet aux fonctions définies par l’utilisateur d’utiliser le contrôle de contexte, de valider les paramètres à l'avance, et de normaliser les données d'entrée avant d'effectuer des calculs.

Note

Pour expr les paramètres parameterMode, les vérifications de type se produisent lorsque le paramètre est référencé dans le corps de la fonction (et non au moment de l’appel de la fonction).

Fonctions de vérification de type disponibles

Les fonctions UDF peuvent utiliser les fonctions suivantes afin de tester les valeurs scalaires. Chaque retour TRUE/FALSE varie selon que la valeur fournie est de ce type.

Catégorie Functions
Numeric ISNUMERIC, ISNUMBER
Double ISDOUBLE
Nombre entier ISINT64, ISINTEGER
Decimal ISDECIMAL, ISCURRENCY
Chaîne ISSTRING, ISTEXT
Booléen ISBOOLEAN, ISLOGICAL
Date et heure ISDATETIME

Exemple : Vérifier si le paramètre est une chaîne

DEFINE
    /// Returns the length of a string, or BLANK if not a string
    FUNCTION StringLength = (
            s
        ) =>
        IF ( ISSTRING ( s ), LEN ( s ), BLANK () )

EVALUATE
{ StringLength ( "hello" ), StringLength ( 123 ) }
// Returns: 5, BLANK

Cela empêche les erreurs et vous permet de décider comment gérer l’entrée non-chaîne dans la fonction (dans cet exemple, retourne BLANK).

Exemple : Accepter plusieurs types de paramètres

DEFINE
    /// Helper 1: get currency name by int64 key
    FUNCTION GetCurrencyNameByKey = (
            k : INT64
        ) =>
        LOOKUPVALUE ( 'Currency'[Currency], 'Currency'[CurrencyKey], k )
    
    /// Helper 2: get currency name by string code
    FUNCTION GetCurrencyNameByCode = (
            code : STRING
        ) =>
        LOOKUPVALUE ( 'Currency'[Currency], 'Currency'[Code], code )
    
    /// Accepts key (int64) or code (string) and returns the currency name
    FUNCTION GetCurrencyName = (
            currency
        ) =>
        IF (
            ISINT64 ( currency ),
            GetCurrencyNameByKey ( currency ),
            GetCurrencyNameByCode ( currency )
        )

EVALUATE
{ GetCurrencyName ( 36 ), GetCurrencyName ( "USD" ) }
// returns "Euro", "US Dollar"

Cet exemple démontre comment utiliser la vérification de type dans les fonctions utilisateur pour sécuriser l'acceptation de plusieurs types d'entrées et garantir un résultat unique et prévisible. GetCurrencyName prend un argument, currencyqui peut être une clé monétaire de nombre entier ou un code monétaire de texte. La fonction vérifie le type d’argument avec ISINT64. Si l’entrée est un entier, elle appelle l’assistance GetCurrencyNameByKey qui recherche le nom de la devise en fonction de la clé monétaire. Si l’entrée n’est pas un entier, elle appelle l’assistance GetCurrencyNameByCode qui recherche le nom monétaire en fonction du code monétaire.

Définir plusieurs fonctions à la fois

Les fonctions définies par l’utilisateur vous permettent de définir plusieurs fonctions dans une requête ou un script unique, ce qui facilite l’organisation de la logique réutilisable. Cela est particulièrement utile lorsque vous souhaitez encapsuler des calculs associés ou des routines d’assistance ensemble. Les fonctions peuvent être évaluées ensemble ou individuellement.

DEFINE
    /// Multiplies two numbers
    FUNCTION Multiply = (
            a,
            b
        ) =>
        a * b

    /// Adds two numbers and 1
    FUNCTION AddOne = (
            x,
            y
        ) =>
        x + y + 1

    /// Returns a random integer between 10 and 100
    FUNCTION RandomInt = () =>
        RANDBETWEEN ( 10, 100 )

EVALUATE
{ Multiply ( 3, 5 ), AddOne ( 1, 2 ), RandomInt () }
// returns 15, 4, 98

Exemple avancé : Conversion monétaire flexible

Pour montrer comment DAX les fonctions définies par l'utilisateur peuvent gérer une logique plus complexe, nous allons examiner cela dans le cadre d'un scénario de conversion de devises. Cet exemple utilise la vérification de type et les fonctions imbriquées pour convertir un montant donné en devise cible à l’aide du taux de change moyen ou de fin de jour pour une date donnée.

createOrReplace
	function ConvertDateToDateKey =  
		( 
			pDate: scalar variant 
		) => 
		YEAR ( pDate ) * 10000 + MONTH ( pDate ) * 100 + DAY ( pDate ) 
	
	function ConvertToCurrency = 
		( 
			pCurrency:scalar variant, 
			pDate: scalar variant, 
			pUseAverageRate: scalar boolean, 
			pAmount: scalar decimal 
		) => 
		var CurrencyKey = 
			EVALUATEANDLOG ( 
				IF ( 
					ISINT64 ( pCurrency ), 
					pCurrency, 
					CALCULATE ( 
						MAX ( 'Currency'[CurrencyKey] ), 
						'Currency'[Code] == pCurrency 
					) 
				) 
				, "CurrencyKey" 
			) 

		var DateKey = 
			EVALUATEANDLOG ( 
				SWITCH ( 
					TRUE, 
					ISINT64 ( pDate ), pDate, 
					ConvertDateToDateKey ( pDate ) 
				) 
				, "DateKey" 
			) 

		var ExchangeRate = 
			EVALUATEANDLOG ( 
				IF ( 
					pUseAverageRate, 
					CALCULATE ( 
						MAX ( 'Currency Rate'[Average Rate] ), 
						'Currency Rate'[DateKey] == DateKey, 
						'Currency Rate'[CurrencyKey] == CurrencyKey 
					), 
					CALCULATE ( 
					MAX ( 'Currency Rate'[End Of Day Rate] ), 
						'Currency Rate'[DateKey] == DateKey, 
						'Currency Rate'[CurrencyKey] == CurrencyKey 
					) 
				) 
				, "ExchangeRate" 
			) 

		var Result = 
			IF ( 
				ISBLANK ( pCurrency ) || ISBLANK ( pDate ) || ISBLANK ( pAmount ), 
				BLANK (), 
				IF ( 
					ISBLANK ( ExchangeRate ) , 
					"no exchange rate available", 
					ExchangeRate * pAmount 
				) 
			) 

		RETURN Result

La ConvertToCurrency fonction accepte les types d’entrée flexibles pour la devise et la date. Les utilisateurs peuvent fournir une clé monétaire ou une clé de date directement ou fournir un code monétaire ou une valeur de date standard. La fonction vérifie le type de chaque entrée et la gère en conséquence : si pCurrency elle est un nombre entier, elle est traitée comme une clé monétaire ; sinon, la fonction suppose un code monétaire et tente de résoudre la clé correspondante. pDate suit un modèle similaire, s’il s’agit d’un nombre entier, il est traité comme une clé de date ; sinon, la fonction suppose qu’elle est une valeur de date standard et est convertie en clé de date à l’aide de la ConvertDateToDateKey fonction d’assistance. Si la fonction ne peut pas déterminer un taux d'échange valide, elle retourne le message « aucun taux de change disponible ».

Cette logique peut ensuite être utilisée pour définir une mesure telle que Total Sales in Local Currency.

Total Sales in Local Currency = 
ConvertToCurrency (
    SELECTEDVALUE ( 'Currency'[Code] ),
    SELECTEDVALUE ( 'Date'[DateKey] ),
    TRUE,
    [Total Sales]
)

Cela peut éventuellement être associé à une chaîne de format dynamique pour afficher le résultat dans le format monétaire approprié.

CALCULATE (
    MAX ( 'Currency'[Format String] ),
    'Currency'[Code] == SELECTEDVALUE ( 'Currency'[Code] )
)

Vous trouverez un exemple de résultat dans la capture d’écran ci-dessous.

Tableau montrant la date complète, la devise, le total des ventes dans la devise locale et le total des ventes.

Considérations et limitations

Les fonctions définies par l’utilisateur sont actuellement en préversion et, pendant la préversion, tenez compte des considérations et limitations suivantes :

Général :

  • Impossible de créer ou de modéliser des fonctions définies par l'utilisateur DAX dans le service.
  • Impossible de masquer/d’afficher une fonction UDF dans le modèle.
  • Impossible de placer les UDF (fonctions définies par l’utilisateur) dans les dossiers d’affichage.
  • Aucun bouton « créer une fonction » dans le ruban.
  • Impossible de combiner des fonctions définies par l’utilisateur avec des traductions.
  • Les fonctions définies par l’utilisateur ne sont pas prises en charge dans les modèles sans tables.

Définition d’une fonction UDF :

  • La récursivité ou la récursivité mutuelle n’est pas prise en charge.
  • La surcharge de fonction n’est pas prise en charge.
  • Les types de retour explicites ne sont pas pris en charge.

Paramètres UDF :

  • Les paramètres facultatifs ne sont pas pris en charge.
  • Les descriptions des paramètres ne sont pas prises en charge.
  • Les fonctions définies par l’utilisateur ne peuvent pas retourner une enum valeur. Les fonctions intégrées qui acceptent enum comme paramètres de fonction ne pourront pas utiliser les UDF dans ce contexte.
  • Les paramètres non liés de type expr ne sont pas évalués.

Prise en charge d’IntelliSense :

  • Bien que les fonctions définies par l’utilisateur puissent être utilisées dans des modèles "live connect" ou composites, il n’existe aucune prise en charge d’IntelliSense.
  • Bien que les fonctions définies par l’utilisateur (UDF) puissent être utilisées dans les calculs visuels, la barre de formule des calculs visuels ne prend pas en charge IntelliSense pour les UDF.
  • La vue TMDL ne prend pas en charge un support IntelliSense approprié pour les UDFs.

Bogues connus

Les problèmes suivants sont actuellement connus et peuvent avoir un impact sur les fonctionnalités :

  • Les références à un objet de modèle tabulaire (par exemple, mesure, table, colonne) dans une fonction UDF ne sont pas automatiquement mises à jour lorsque ces objets sont renommés. Si vous renommez un objet dont dépend une fonction UDF, le corps de la fonction contiendra toujours l’ancien nom. Vous devez modifier manuellement l’expression UDF pour mettre à jour toutes les références à l’objet renommé.
  • Certains scénarios avancés impliquant des fonctions définies par l’utilisateur peuvent entraîner des incohérences d’analyse. Par exemple, les utilisateurs peuvent voir des soulignements rouges ou des erreurs de validation lorsque des colonnes sont passées en tant que expr paramètres ou en utilisant des références de colonnes non qualifiées.