Freigeben über


DAX benutzerdefinierte Funktionen (Vorschau)

Hinweis

DAX Benutzerdefinierte Funktionen befinden sich derzeit in der Vorschau.

DAX Mit benutzerdefinierten Funktionen (USER-Defined Functions, UDFs) können Sie Logik packen DAX und wie jede andere Funktion wiederverwenden DAX . UDFs stellen ein neues FUNCTION Schlüsselwort, optionale Parameter (Skalar, Tabelle und Verweise) und Typüberprüfungshilfsprogramme vor, die die Erstellung sicherer und klarer machen. Nachdem Sie eine UDF definiert haben, können Sie sie in einem Measure, einer berechneten Spalte, einer visuellen Berechnung oder sogar in anderen benutzerdefinierten Funktionen verwenden. Benutzer können Geschäftsregeln zentralisieren, die Wartung verbessern und Berechnungen im Laufe der Zeit sicher weiterentwickeln. Funktionen sind erstklassige Modellobjekte, die Sie in der Abfrageansicht und inDAX der TMDL-Ansicht erstellen und verwalten können, und sie können im Modell-Explorer unter dem Knoten "Funktionen" angezeigt werden.

Aktivieren benutzerdefinierter Funktionen

So versuchen Sie UDFs in Desktop:

  1. Wechseln Sie zu "Dateioptionen>" und "Einstellungsoptionen">.
  2. Wählen Sie Vorschaufeatures aus, und überprüfen Sie DAX benutzerdefinierte Funktionen.
  3. Wählen Sie "OK" aus, und starten Sie Power BI Desktop neu .

Definieren und Verwalten von benutzerdefinierten Funktionen

Es gibt mehrere Speicherorte zum Definieren und Verwalten von Funktionen:

  • DAX Abfrageansicht (DQV). Definieren und Ändern von Funktionen in DQV. DQV umfasst auch Schnellabfragen im Kontextmenü (Auswerten, Definieren und Auswerten sowie alle Funktionen in diesem Modell definieren), um UDFs schnell zu testen und zu verwalten.
  • TMDL-Ansicht. UDFs können auch in TMDL erstellt und bearbeitet werden. Die TMDL-Ansicht enthält auch das Kontextmenü Skript TMDL zu.
  • Modell-Explorer. Neue Funktionen können erstellt werden, und vorhandene Funktionen können mithilfe der Bearbeitungsleiste geändert werden. Vorhandene Funktionen können im Modell-Explorer unter dem Knoten "Funktionen" angezeigt werden.

Wenn Sie eine UDF definieren, befolgen Sie die folgenden Benennungsanforderungen:

Funktionsnamen:

  • Muss innerhalb des Modells wohlgeformt und einzigartig sein.
  • Kann Punkte für Namespacing (z. B. Microsoft.PowerBI.MyFunc) enthalten. Es kann nicht mit einem Punkt beginnen oder enden oder aufeinander folgende Punkte aufweisen.
  • Außer Punkten (.) können Namen nur alphanumerische Zeichen oder Unterstriche enthalten. Keine Leerzeichen oder Sonderzeichen zulässig.
  • Darf nicht mit eingebauten DAX Funktionen oder reservierten Wörtern in Konflikt stehen (z. B. messen, funktion, definieren).

Parameternamen:

  • Kann nur alphanumerische Zeichen oder Unterstriche enthalten. Zeiträume sind nicht zulässig.
  • Darf kein reserviertes Wort sein.

Verwenden der DAX Abfrageansicht

Sie können benutzerdefinierte Funktionen in der DAX Abfrageansicht definieren, aktualisieren und auswerten. Weitere Informationen zur DAX Abfrageansicht finden Sie in der DAX Abfrageansicht.

Allgemeines Formular

DEFINE
    /// Optional description above the function
    /// @param {ParameterType} ParameterName - ParameterDescription
    /// ...
    /// @returns ReturnDescription
    FUNCTION <FunctionName> = ( [ParameterName]: [ParameterType], ... ) => <FunctionBody>

Tipp

Dokumentieren Sie Mithilfe von JSDoc-Blocktags Ihre Funktionen mit Beschreibungen, Parameternamen und Typen, und geben Sie Informationen zurück, um sie einfacher zu nutzen. Beachten Sie die /// für Funktionsbeschreibungen. Einzeilige (//) oder mehrzeilige (/* */) Kommentare werden in IntelliSense-Funktionsbeschreibungen nicht angezeigt.

Beispiel: Einfache Steuerfunktion

DEFINE
    /// AddTax takes in amount and returns amount including tax
    /// @param {NUMERIC} amount - The pre-tax value to which tax will be applied
    /// @returns The amount including 10% tax
    FUNCTION AddTax = 
        ( amount : NUMERIC ) =>
            amount * 1.1

EVALUATE
{ AddTax ( 10 ) }
// Returns 11

Speichern im Modell

So speichern Sie eine UDF aus der DAX Abfrageansicht zum Modell:

  • Klicken Sie auf "Modell aktualisieren" mit Änderungen , um alle UDFs in der Abfrage zu speichern.
  • Oder klicken Sie auf "Modell aktualisieren": Fügen Sie oberhalb der definierten Funktion neue Funktion hinzu, um eine einzelne UDF zu speichern.

Screenshot der DAX Abfrageansicht in Power BI Desktop, wobei zwei Speicherorte hervorgehoben werden, an denen Sie eine benutzerdefinierte Funktion speichern können. Das erste ist das Updatemodell mit der Schaltfläche

Verwenden der TMDL-Ansicht

Sie können benutzerdefinierte Funktionen in der TMDL-Ansicht definieren und/oder aktualisieren. Weitere Informationen zur TMDL-Ansicht finden Sie in der TMDL-Ansicht.

Allgemeines Formular

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

Beispiel: Einfache Steuerfunktion

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

Speichern im Modell

Klicken Sie auf die Schaltfläche Übernehmen oben in der Ansicht, um alle UDFs aus dem Skript im Modell zu speichern.

Screenshot der TMDL-Ansicht in Power BI Desktop, in der die Schaltfläche "Anwenden" oben in der TMDL-Ansicht hervorgehoben wird. Dies ist der Ort, an dem Sie eine benutzerdefinierte Funktion speichern können.

Verwenden des TMDL-Skripts in einem Power BI-Projekt

UDFs sind auch beim Verwenden eines Power BI-Projekts im Semantikmodell-TMDL-Skript enthalten. Sie befinden sich im functions.tmdlDefinitionsordner .

Visual Studio Code-Screenshot eines Power BI-Projekts. Explorer ist für den Semantikmodellordner geöffnet.

Verwenden des Modell-Explorers

Sie können alle benutzerdefinierten Funktionen im Modell im Modell-Explorer unter dem Knoten "Funktionen" anzeigen. Weitere Informationen zum Modell-Explorer finden Sie im Modell-Explorer.

Modell-Explorer-Bereich in Power BI Desktop mit dem erweiterten Knoten

In DAX der Abfrageansicht können Sie Schnellabfragen im Kontextmenü einer UDF im Modell-Explorer verwenden, um Funktionen einfach zu definieren und auszuwerten.

Der Bereich

In der TMDL-Ansicht können Sie Funktionen per Drag & Drop in den Arbeitsbereich verschieben oder im Kontextmenü einer UDF im Modell-Explorer Script TMDL verwenden, um Skripts zu erzeugen.

Der Bereich

Verwenden von DMVs zum Untersuchen von UDFs

Sie können UDFs in Ihrem Modell mithilfe von dynamischen Verwaltungsansichten (Dynamic Management Views, DMVs) prüfen. Mit diesen Ansichten können Sie Informationen zu Funktionen abfragen, einschließlich UDFs.

Sie können die Funktion INFO.USERDEFINEDFUNCTIONS verwenden, um die UDFs im Modell zu überprüfen. Diese Funktion gibt vollständige Metadaten zurück und erfordert Schreibberechtigungen.

EVALUATE INFO.USERDEFINEDFUNCTIONS()

Alternativ können Sie die Funktion INFO.FUNKTOKEN verwenden, um UDF-Namen und eingeschränkte Metadaten zurückzugeben.

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

Verwenden einer benutzerdefinierten Funktion

Nachdem eine UDF definiert und im Modell gespeichert wurde, können Sie sie aus Measures, berechneten Spalten, visuellen Berechnungen und anderen UDFs aufrufen. Dies funktioniert genauso wie das Aufrufen integrierter DAX Funktionen.

Aufrufen einer UDF in einem Maß

Verwenden Sie eine UDF in einem Measure, um wiederverwendbare Logik mit vollständigem Filterkontext anzuwenden.

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

Das Beispielmaß wird in der folgenden Tabelle gezeigt:

Tabelle mit

Aufrufen einer UDF in einer berechneten Spalte

UDFs können in einer berechneten Spalte verwendet werden, um wiederverwendbare Logik auf jede Zeile in einer Tabelle anzuwenden.

Hinweis

Wenn Sie eine UDF in einer berechneten Spalte verwenden, stellen Sie sicher, dass die Funktion einen Skalar eines konsistenten Typs zurückgibt. Weitere Informationen finden Sie unter Parameter . Konvertieren Sie das Ergebnis bei Bedarf mit CONVERT oder ähnlichen Funktionen in den gewünschten Typ.

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

Wir können dieses Beispielmaß sehen, das in der folgenden Tabelle verwendet wird:

Tabelle mit Verkaufsbetrag und Verkaufsbetrag mit Steuern. Der Verkaufsbetrag mit Steuern ist hervorgehoben. Der Bereich

Aufrufen einer UDF in einer visuellen Berechnung

Sie können UDFs in einer visuellen Berechnung verwenden, um Logik direkt auf das visuelle Element anzuwenden. Weitere Informationen zu visuellen Berechnungen finden Sie unter Visuelle Berechnungen.

Hinweis

Visuelle Berechnungen funktionieren nur für Felder, die im visuellen Element vorhanden sind. Sie können nicht auf Modellobjekte zugreifen, die nicht Teil des visuellen Elements sind, und Sie können modellbezogene Objekte (z. B. Spalten oder Measures, die nicht im visuellen Element sind) in diesem Kontext nicht an eine UDF übergeben.

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

Wir können dieses Beispielmaß in der folgenden Tabelle sehen:

Im Bearbeitungsmodus für visuelle Berechnungen. Tabelle mit Verkaufsbetrag und Verkaufsbetrag mit Steuern. Der Verkaufsbetrag mit Steuern ist hervorgehoben. Die visuelle Berechnungsformel für den Umsatzbetrag mit Steuern ist hervorgehoben.

Aufrufen einer UDF in einer anderen UDF

Sie können UDFs verschachteln, indem Sie eine Funktion aus einer anderen aufrufen. In diesem Beispiel definieren wir unsere einfache AddTax UDF und rufen sie in einer anderen UDF auf. 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

Die Parameter

DAX UDFs können null oder mehr Parameter akzeptieren. Wenn Sie Parameter für eine UDF definieren, können Sie optional Typhinweise für jeden Parameter angeben:

  • Typ: welche Art von Wert der Parameter akzeptiert (AnyVal, Scalar, , Table, AnyRef, CalendarRef, oder ColumnRefMeasureRefTableRef ).
  • Untertyp (nur für Skalartyp): der spezifische skalare Datentyp (Variant, Int64, , Decimal, Double, String, DateTime, Booleanoder Numeric).
  • ParameterMode: Wenn das Argument ausgewertet wird (val oder expr).

Typenhinweise befinden sich in der Form: [type] [subtype] [parameterMode]

Sie können alle, einige oder keine dieser Typhinweise für jeden Parameter einschließen, um Ihre Funktionen sicherer und vorhersehbarer auf Anrufwebsites zu machen. Wenn Sie alles weglassen und einfach den Parameternamen schreiben, verhält es sich wie AnyVal val, d. h., das Argument wird sofort zur Aufrufzeit ausgewertet. Dies ist nützlich für einfache Funktionen.

Typ

Der Typ definiert die Kategorie des Arguments, das der Parameter akzeptiert, und ob er als Wert oder Ausdruck übergeben wird.

Es gibt zwei Typenfamilien in DAX UDF-Parametern: Werttypen und Ausdruckstypen:

  • Werttypen: Dieses Argument wird sofort ausgewertet (eifrige Auswertung), wenn die Funktion aufgerufen wird und der resultierende Wert an die Funktion übergeben wird.
    • AnyVal: Akzeptiert einen Skalar oder eine Tabelle. Dies ist die Standardeinstellung, wenn Sie den Typ für einen Parameter weglassen.
    • Scalar: Akzeptiert einen skalaren Wert (kann zusätzlich einen Untertyp hinzufügen).
    • Table: Akzeptiert eine Tabelle.
  • Ausdruckstypen: Dieses Argument übergibt einen nicht ausgewerteten Ausdruck (faule Auswertung). Die Funktion entscheidet, wann und in welchem Kontext sie ausgewertet werden soll. Dies ist erforderlich für Referenzparameter und nützlich, wenn Sie den Filterkontext steuern müssen (z. B. innerhalb CALCULATE). expr Typen können Verweise auf eine Spalte, eine Tabelle, einen Kalender oder eine Maßnahme sein.
    • AnyRef: Akzeptiert beliebige Verweise. Es ist gleichbedeutend mit dem Nicht-Angeben eines Ausdruckstyps.
    • CalendarRef: Akzeptiert einen Verweis auf einen Kalender.
    • ColumnRef: Akzeptiert einen Verweis auf eine Spalte.
    • MeasureRef: Erlaubt einen Verweis auf ein Maß.
    • TableRef: Akzeptiert einen Verweis auf eine Tabelle.

Werttypen (AnyVal, Scalar, Table) unterstützen die implizite Typumwandlung. Ausdruckstypen (AnyRef, CalendarRef, ColumnRef, MeasureRef, TableRef) tun es nicht.

Subtyp

Mit dem Untertyp können Sie einen bestimmten Scalar Datentyp definieren. Wenn Sie einen Untertyp definieren, müssen Sie den Parameter nicht explizit als Scalar Typ definieren, wird dies automatisch angenommen.

Untertypen sind:

  • Variant: Akzeptiert beliebigen Skalar.
  • Int64: Akzeptiert eine ganze Zahl.
  • Decimal: Akzeptiert eine Dezimalzahl mit fester Genauigkeit (z. B. Währung oder Geld).
  • Double: Akzeptiert eine Gleitkommadezimalzahl.
  • String: Akzeptiert Text.
  • DateTime: Akzeptiert Datum/Uhrzeit.
  • Boolean: Akzeptiert TRUE/FALSE.
  • Numeric: Akzeptiert numerische Werte (Int64, Decimaloder Double Untertypen)

ParameterModus

ParameterMode steuert, wann und wo der Parameterausdruck ausgewertet wird. Diese lauten wie folgt:

  • val (eifrige Auswertung): Der Ausdruck wird einmal ausgewertet, bevor die Funktion aufruft wird. Der resultierende Wert wird dann an die Funktion übergeben. Dies ist üblich für einfache Skalar- oder Tabelleneingaben. Dies ist die Standardeinstellung, wenn Sie "parameterMode" für einen Parameter weglassen.
  • expr (faule Auswertung): Der Ausdruck wird innerhalb der Funktion ausgewertet, potenziell in einem anderen Kontext (z. B. Zeilenkontext oder Filterkontext) und möglicherweise mehrmals, wenn mehrfach oder innerhalb von Iterationen verwiesen wird. Dies ist für die Referenzparameter erforderlich und nützlich, wenn Sie den Kontext der Auswertung steuern müssen.

Der Scalar Typ kann entweder val oder expr. Verwenden Sie val, wenn der Skalar einmal im Kontext des Aufrufers ausgewertet werden soll. Verwenden Sie diese Funktion expr , wenn Sie die Auswertung zurückstellen und möglicherweise den Kontext innerhalb der Funktion anwenden möchten. Siehe Beispiel: Tabellenparameter als Beispiel.

Der Ausdruckstyp (AnyRef, ColumnRef, usw.) muss expr sein, da seine Bezüge (Spalten, Tabellen, Measures, usw.) im Kontext der Funktion ausgewertet werden müssen.

In der folgenden Tabelle wird der effektive/zulässige ParameterMode zusammengefasst:

Typ ParameterMode nicht angegeben ParameterModus: val ParameterMode: expr
(Nicht angegeben) / AnyVal val val expr
Scalar, Table val val expr
AnyRef expr Nicht zulässig expr
CalendarRef ColumnRef MeasureRef TableRef expr Nicht zulässig expr

Beispiel: Typumwandlung

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

Dies verwendet einen Scalar Typ, Int64 Untertyp und val ParameterMode für vorhersagbare Rundung und Text-zu-Zahlen-Koersion, und stellt sicher, dass alle Ausdrücke eifrig ausgewertet werden. Sie können dies auch erreichen, indem Sie einfach den Int64 Untertyp einschließen, wie im folgenden Beispiel dargestellt. Nicht numerische Zeichenfolgen führen zu einem Fehler.

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

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

Beispiel: Tabellenparameter (Wert vs Ausdruck)

Zur Veranschaulichung, wie sich der UDF-ParameterMode auf den Filterkontext auswirkt, betrachten Sie zwei Funktionen, die beide Zeilen in der Tabelle "Sales" zählen. Beide verwenden CALCULATETABLE(t, ALL('Date')) in ihren Körpern, aber ein Parameter wird als val (eifrige Auswertung) und der andere als expr (faule Auswertung) deklariert.

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 gibt nur die Anzahl der Verkäufe für FY2020 zurück. Die Tabelle "Umsatz" wird bereits nach dem Jahr gefiltert, bevor Sie die Funktion eingeben, sodass ALL('Date') innerhalb der Funktion keine Auswirkung hat.

CountRowsLater gibt die Anzahl der Verkäufe für alle Jahre zurück. Die Funktion empfängt einen nicht ausgewerteten Tabellenausdruck und wertet ihn unter ALL('Date')aus, wobei der externe Jahresfilter entfernt wird.

Typüberprüfung

Die Typüberprüfung in UDFs kann mit neuen und vorhandenen Typüberprüfungsfunktionen erfolgen, die Sie innerhalb des Funktionstexts aufrufen können, um den Laufzeittyp der übergebenen Parameter zu bestätigen. Auf diese Weise können UDFs Kontextsteuerelemente verwenden, Parameter vorab überprüfen, Eingaben vor der Berechnung normalisieren.

Hinweis

Bei expr ParameterMode-Parametern treten Typüberprüfungen auf, wenn der Parameter im Funktionstext referenziert wird (nicht zur Funktionsaufrufzeit).

Verfügbare Typüberprüfungsfunktionen

UDFs können die folgenden Funktionen verwenden, um skalare Werte zu testen. Jede Rückgabe TRUE/FALSE, je nachdem, ob der angegebene Wert zu diesem Typ gehört.

Kategorie Funktionen
Numerisch ISNUMERIC, ISNUMBER
Double ISDOUBLE
Ganze Zahl ISINT64, ISINTEGER
Decimal ISTDEZIMAL, ISTWÄHRUNG
String ISSTRING, ISTEXT
Boolean ISBOOLEAN, ISLOGICAL
Datum und Uhrzeit ISDATETIME

Beispiel: Überprüfen, ob der Parameter eine Zeichenfolge ist

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

Dadurch werden Fehler vermieden, und Sie können entscheiden, wie die nicht-zeichenfolgen Eingaben in die Funktion behandelt werden (in diesem Beispiel wird BLANK zurückgegeben).

Beispiel: Akzeptieren mehrerer Parametertypen

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"

In diesem Beispiel wird gezeigt, wie Sie die Typüberprüfung in UDFs verwenden, um mehrere Eingabetypen sicher zu akzeptieren und ein einzelnes, vorhersagbares Ergebnis zurückzugeben. GetCurrencyName verwendet ein Argument, currencydas entweder ein Ganzzahlwährungsschlüssel oder ein Textwährungscode sein kann. Die Funktion überprüft den Argumenttyp mit ISINT64. Wenn die Eingabe eine ganze Zahl ist, ruft sie das Hilfsprogramm GetCurrencyNameByKey auf, das den Währungsnamen basierend auf dem Währungsschlüssel nachschlagen soll. Wenn die Eingabe keine ganze Zahl ist, ruft sie das Hilfsprogramm GetCurrencyNameByCode auf, das den Währungsnamen basierend auf dem Währungscode nachschlagen soll.

Nützliche Informationsfunktionen

Die folgenden Informationsfunktionen sind beim Erstellen von UDFs nützlich:

  • TABLEOF: Gibt die vollständige Tabelle zurück, die einer bestimmten Spalte, einer bestimmten Messung oder einem Kalender zugeordnet ist.
  • NAMEOF: Gibt den Namen einer Tabelle, Spalte, eines Maßes oder eines Kalenders als Textzeichenfolge zurück.

Beispiel: Eine MODEX-Funktion

Das folgende Beispiel zeigt eine grundlegende Implementierung einer MODEX-Funktion, die den am häufigsten vorkommenden Wert eines Ausdrucks zurückgibt, der über eine Tabelle ausgewertet wird. Es verwendet TABLEOF, um die richtige Tabelle automatisch aus dem angegebenen Verweis aufzulösen.

DEFINE
    FUNCTION MODEX = (
            e : ANYREF
        ) =>
        VAR newTable =
            ADDCOLUMNS ( TABLEOF ( e ), "expr", e )
        VAR freqTable =
            GROUPBY ( newTable, [expr], "count", SUMX ( CURRENTGROUP (), 1 ) )
        VAR maxCount =
            MAXX ( freqTable, [count] )
        VAR topResults =
            FILTER ( freqTable, [count] = maxCount )
        RETURN
            SELECTCOLUMNS ( topResults, "expr", [expr] )

EVALUATE
MODEX ( [Total Sales] )

Gleichzeitiges Definieren mehrerer Funktionen

UDFs ermöglichen es Ihnen, mehrere Funktionen in einer einzelnen Abfrage oder einem einzigen Skript zu definieren, wodurch es einfach ist, wiederverwendbare Logik zu organisieren. Dies ist besonders hilfreich, wenn Sie verwandte Berechnungen oder Hilfsroutinen zusammen kapseln möchten. Funktionen können zusammen oder einzeln ausgewertet werden.

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

Erweitertes Beispiel: Flexible Währungsumrechnung

Um zu zeigen, wie DAX UDFs komplexere Logik verarbeiten können, betrachten wir ein Währungskonvertierungsszenario. In diesem Beispiel werden Typüberprüfungen und geschachtelte Funktionen verwendet, um einen bestimmten Betrag in eine Zielwährung zu konvertieren, indem entweder der durchschnittliche oder der Schlusskurs für ein bestimmtes Datum verwendet wird.

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

Die ConvertToCurrency Funktion akzeptiert flexible Eingabetypen für Währung und Datum. Benutzer können entweder einen Währungsschlüssel oder einen Datumsschlüssel direkt bereitstellen oder einen Währungscode oder einen Standarddatumswert angeben. Die Funktion überprüft den Typ der einzelnen Eingaben und behandelt sie entsprechend: Wenn pCurrency es sich um eine ganze Zahl handelt, wird sie als Währungsschlüssel behandelt. Andernfalls geht die Funktion von einem Währungscode aus und versucht, den entsprechenden Schlüssel aufzulösen. pDate folgt einem ähnlichen Muster, wenn es sich um eine ganze Zahl handelt, wird es als Datumsschlüssel behandelt; andernfalls wird davon ausgegangen, dass es sich um einen Standarddatumswert handelt und mithilfe der ConvertDateToDateKey Hilfsfunktion in einen Datumsschlüssel konvertiert wird. Wenn die Funktion keinen gültigen Exchnage-Kurs ermitteln kann, wird die Meldung "Kein Wechselkurs verfügbar" zurückgegeben.

Diese Logik kann dann verwendet werden, um ein Measure wie "Gesamtumsatz in lokaler Währung" zu definieren.

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

Dies kann optional mit einer dynamischen Formatzeichenfolge kombiniert werden, um das Ergebnis im entsprechenden Währungsformat anzuzeigen.

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

Ein Beispielergebnis ist im folgenden Screenshot zu sehen.

Tabelle mit vollständigem Datum, Währung, Gesamtumsatz in lokaler Währung und Gesamtumsatz.

Überlegungen und Einschränkungen

Benutzerdefinierte Funktionen befinden sich derzeit in der Vorschau, und beachten Sie bitte die folgenden Überlegungen und Einschränkungen:

Allgemein:

  • UDFs können im Dienst nicht verfasst oder modelliert werden DAX .

  • Eine UDF im Modell kann nicht ausgeblendet/eingeblendet werden.

  • UDFs können nicht in Anzeigeordner abgelegt werden.

  • Keine Schaltfläche 'Funktion erstellen' im Menüband.

  • UdFs können nicht mit Übersetzungen kombiniert werden.

  • UDFs werden in Modellen ohne Tabellen nicht unterstützt.

  • Object-Level Security (OLS) wird nicht auf Funktionen übertragen oder umgekehrt. Betrachten Sie beispielsweise die folgende Funktion F , die sich auf gesichertes Maß MyMeasurebezieht:

    function F = () => [MyMeasure] + 42
    

    Wenn die MyMeasure unter Verwendung von Sicherheit auf Objektebene gesichert wird, wird die Funktion F nicht automatisch gesichert. Wenn F unter einer Identität ohne Zugriff auf MyMeasure ausgeführt wird, fungiert er so, als wäre MyMeasure nicht vorhanden. Es wird empfohlen, sichere Objekte nicht in den Bezeichnungen und Beschreibungen von Funktionen preiszugeben.

  • Formelkorrektur und Abhängigkeitsberechnung werden unterstützt, wobei es eine bekannte Einschränkung bei nicht qualifizierten Namen gibt. Da nicht qualifizierte Namen als Maßverweise interpretiert werden, können sie nicht zuverlässig behoben oder in die Abhängigkeitsnachverfolgung einbezogen werden, wenn sie auf Spalten verweisen sollen. Ein nicht qualifizierter Name ist ein Objektverweis, der kein Tabellenpräfix enthält.

  • CalendarRef, ColumnRef, MeasureRef und TableRef Typhinweise könnten möglicherweise bei allen Funktionsaufrufen nicht akzeptiert werden, während wir uns im Vorschaumodus befinden. Der Benutzer kann auf AnyRef zurückgreifen.

Definieren einer UDF:

  • Rekursion oder gegenseitige Rekursion wird nicht unterstützt.
  • Funktionsüberladungen werden nicht unterstützt.
  • Explizite Rückgabetypen werden nicht unterstützt.

UDF-Parameter:

  • Optionale Parameter werden nicht unterstützt.
  • Parameterbeschreibungen werden nicht unterstützt.
  • UDFs können keinen enum Wert zurückgeben. Integrierte Funktionen, die enum-Werte als Funktionsparameter akzeptieren, können in diesem Kontext keine UDFs verwenden.
  • Ungebundene Parameter des Typhinweiss expr werden nicht ausgewertet.

IntelliSense-Unterstützung:

  • Obwohl UDFs in Live Connect- oder Verbundmodellen verwendet werden können, gibt es keine IntelliSense-Unterstützung.
  • Obwohl UDFs in visuellen Berechnungen verwendet werden können, verfügt die Bearbeitungsleiste für visuelle Berechnungen über keine IntelliSense-Unterstützung für UDFs.
  • TMDL View verfügt über eingeschränkte IntelliSense-Unterstützung für UDFs.

Bekannte Fehler

Die folgenden Probleme sind derzeit bekannt und können sich auf die Funktionalität auswirken:

  • Bestimmte erweiterte Szenarien mit UDFs können zu Parserinkonsistenzen führen. Benutzer können beispielsweise rote Unterstreichungen oder Überprüfungsfehler sehen, wenn Spalten als expr Parameter übergeben oder nicht qualifizierte Spaltenverweise verwendet werden.