DAX определяемые пользователем функции (предварительная версия)

Замечание

DAX Определяемые пользователем функции в настоящее время находятся в предварительной версии.

DAX Пользовательские определения функций позволяют упаковать DAX логику и повторно использовать её как любую другую DAX функцию. Определяемые пользователем функции вводят новое FUNCTION ключевое слово, необязательные параметры (скалярные, табличные и ссылки), а также вспомогательные средства проверки типов, которые делают разработку безопаснее и понятнее. После определения UDF его можно использовать в мерах, вычисляемых столбцах, визуальном вычислении или даже других пользовательских функциях. Пользователи могут централизировать бизнес-правила, повысить удобство обслуживания и безопасно развивать вычисления с течением времени. Функции — это объекты модели первого класса, которые можно создавать и управлять DAX в представлении запросов и представлении TMDL, и их можно просматривать в обозревателе моделей в узле "Функции ".

Включение определяемых пользователем функций

Чтобы попробовать определяемые пользователем функции на настольном приложении, выполните следующее:

  1. Перейдите к Файл > Параметры и настройки > Параметры.
  2. Выберите функции предварительной версии и проверьте DAX определяемые пользователем функции.
  3. Нажмите кнопку "ОК " и перезапустите Power BI Desktop.

Определение определяемых пользователем функций и управление ими

Существует несколько расположений для определения функций и управления ими:

  • DAX представление данных запроса (DQV). Определение и изменение функций в DQV. DQV также включает быстрые запросы в контекстном меню (оценка, определение и оценка всех функций в этой модели), которые помогут вам быстро протестировать пользовательские функции и управлять ими.
  • Отображение TMDL. Определяемые пользователем функции можно также создавать и редактировать в TMDL. Представление TMDL также включает контекстное меню Script TMDL в.
  • Обозреватель моделей. Новые функции можно создать, а существующие функции можно изменить с помощью строки формул. Существующие функции можно просмотреть в узле "Функции " в обозревателе моделей.

При определении UDF выполните следующие требования к именованию:

Имена функций:

  • Должен быть правильно сформирован и уникален в рамках модели.
  • Может включать точки для разделения пространств имен (например, Microsoft.PowerBI.MyFunc). Не удается начать или завершить период или иметь последовательные периоды.
  • Кроме периодов, имена могут содержать только буквенно-цифровые символы или символы подчеркивания. Пробелы или специальные символы не разрешены.
  • Не должен конфликтовировать со встроенными DAX функциями или зарезервированными словами (например, мерой, функцией, определением).

Имена параметров:

  • Может содержать только буквенно-цифровые символы или символы подчеркивания. Периоды не допускаются.
  • Это не должно быть зарезервированным словом.

Использование DAX представления запроса

Вы можете определять, обновлять и оценивать определяемые пользователем функции в DAX представлении запросов. Для получения дополнительной информации о представлении запросов DAX, см. раздел представление запросов DAX.

Общая форма

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

Подсказка

Используя теги блоков JSDoc, задокументируйте функции, добавив описания, имена и типы параметров, а также информацию о возвращаемом значении, чтобы облегчить их использование. Обратите внимание на /// описания функций. Однострочные (//) или многострочные комментарии/* */ () не будут отображаться в описаниях функций IntelliSense.

Пример: простая налоговая функция

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

Сохранение в модель

Чтобы сохранить UDF из DAX представления запроса в модель:

  • Щелкните Обновить модель с учетом изменений, чтобы сохранить все пользовательские функции в запросе.
  • Или нажмите кнопку "Обновить модель": добавьте новую функцию над определенной функцией, чтобы сохранить один UDF.

DAX Снимок экрана: представление запроса в Power BI Desktop с выделением двух расположений, где можно сохранить определяемую пользователем функцию. Первым является модель обновления с кнопкой

Использование представления TMDL

В представлении TMDL можно определить и (или) обновить определяемые пользователем функции. Дополнительные сведения о представлении TMDL см. в представлении TMDL.

Общая форма

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

Пример: простая налоговая функция

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

Сохранение в модель

Нажмите кнопку "Применить" в верхней части окна, чтобы сохранить все ОПФ в скрипте в модель.

Снимок экрана: представление TMDL в Power BI Desktop, выделение кнопки

Использование скрипта TMDL в проекте Power BI

Определяемые пользователем функции также включаются в семантическую модель скрипта TMDL при использовании проекта Power BI. Их можно найти в functions.tmdl папке определения .

Снимок экрана Visual Studio Code для проекта Power BI. Обозреватель открыт для папки семантической модели. Функция functions.tmdl открыта в редакторе кода. Отображаются три функции: CustomerLifetimeValue, AverageOrderValue и AddTax.

Использование обозревателя моделей

Вы можете просмотреть все пользовательские функции в модели из обозревателя моделей в узле "Функции ". Дополнительные сведения о обозревателе моделей см. в обозревателе моделей.

Панель обозревателя моделей в Power BI Desktop с развернутым узлом функций. Перечислены три пользовательские функции: AddTax, AverageOrderValue и CustomerLifetimeValue.

В DAX представлении запросов можно использовать быстрые запросы в меню правой кнопкой мыши uDF в обозревателе моделей, чтобы легко определять и оценивать функции.

В области обозревателя моделей в Power BI Desktop отображается развернутый узел функций. Открыты два контекстных меню: первое меню содержит Быстрые запросы, Переименование, Удаление из модели, Скрытие в представлении отчета, Показать все, Свернуть все и Развернуть все. Быстрые запросы выделены и выбраны. Второе меню выделено и предлагает варианты быстрых запросов

В представлении TMDL можно перетащить и отпустить функции на холст или использовать Script TMDL в контекстном меню UDF в обозревателе моделей для создания скриптов.

В области обозревателя моделей в Power BI Desktop отображается развернутый узел функций. Открываются два контекстных меню: первое меню предоставляет опции: Выполнить скрипт для TMDL, Переименовать, Удалить из модели, Скрыть в режиме отчета, Показать все, Свернуть все и Развернуть все. Выполнить скрипт для TMDL выделено и выбрано. Второе меню выделено и предлагает параметры: вкладка

Использование динамических административных представлений для проверки определяемых пользователем функций

Вы можете инспектировать определяемые пользователем функции в модели с помощью динамических административных представлений (DMVs). Эти представления позволяют запрашивать сведения о функциях, в том числе UDF.

Вы можете использовать функцию INFO.USERDEFINEDFUNCTIONS для проверки определяемых пользователем функций в модели. Эта функция возвращает полные метаданные и требует разрешения на запись.

EVALUATE INFO.USERDEFINEDFUNCTIONS()

Кроме того, вы можете использовать функцию INFO.FUNCTIONS для возврата имен UDF и ограниченной метаданных.

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

Использование определяемой пользователем функции

После определения и сохранения UDF в модели его можно вызвать из мер, вычисляемых столбцов, визуальных вычислений и других UDF. Это работает так же, как вызов встроенных DAX функций.

Вызов UDF в меру

Используйте UDF в меру для применения повторно используемой логики с полным контекстом фильтра.

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

Пример меры показан в таблице ниже:

Таблица с общими продажами и общими продажами с налогом. Выделен общий объем продаж с налогом. Откроется область визуализаций. Общее количество продаж с налогом выделено в поле

Вызов UDF в вычисляемом столбце

Определяемые пользователем функции можно использовать в вычисляемом столбце для применения повторно используемой логики к каждой строке таблицы.

Замечание

При использовании UDF в вычисляемом столбце убедитесь, что функция возвращает скаляр согласованного типа. Дополнительные сведения см. в параметрах . При необходимости преобразуйте результат в нужный тип с помощью CONVERT или аналогичных функций.

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

Мы видим этот пример меры, используемой в таблице ниже:

Таблица с суммой продаж и суммой продаж с налогом. Выделена сумма продаж с налогом. Откроется область визуализаций. Сумма продаж с налогом выделена в поле

Вызов UDF в визуальном вычислении

Вы можете использовать определяемые пользователем функции в визуальном вычислении для применения логики непосредственно к визуальному элементу. Дополнительные сведения о визуальных вычислениях см. в разделе "Визуальные вычисления".

Замечание

Визуальные вычисления работают только с полями, присутствующих в визуальном элементе. Они не могут получить доступ к объектам модели, которые не являются частью визуального элемента, и вы не можете передать объекты модели (например, столбцы или меры, не входящие в визуальный элемент) в UDF в этом контексте.

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

Этот пример меры можно увидеть в таблице ниже:

В режиме редактирования визуального вычисления. Таблица с суммой продаж и суммой продаж с налогом. Выделена сумма продаж с налогом. Выделена формула визуального вычисления для суммы продаж с налогом.

Вызов пользовательской функции в другой пользовательской функции

Вы можете вложить пользовательские функции, вызывая одну функцию из другой. В этом примере мы определим простую AddTax UDF и вызовем её в другой 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

Параметры

DAX Определяемые пользователем функции могут принимать ноль или больше параметров. При определении параметров для UDF можно при необходимости указать указания типов для каждого параметра:

  • Тип: какой тип значения принимает параметр (AnyVal, , Scalar, Table, AnyRef, CalendarRefColumnRefMeasureRefили ).TableRef
  • Подтип (только для скалярного типа): конкретный скалярный тип данных (Variant, , Int64, DecimalDouble, StringDateTimeBooleanили).Numeric
  • ParameterMode: при оценке аргумента (val или expr).

Подсказки типов находятся в форме: [type] [subtype] [parameterMode]

Вы можете включить все, некоторые или ни одну из этих типовых подсказок для каждого параметра, чтобы сделать ваши функции более безопасными и предсказуемыми в местах вызова. Если вы опустите все и просто укажете имя параметра, это ведёт себя как AnyVal val, что означает, что аргумент вычисляется немедленно в момент вызова. Это полезно для простых функций.

Тип

Тип определяет категорию аргумента, который принимает параметр и передается ли он как значение или выражение.

В параметрах UDF есть два семейства DAX типов: типы значений и типы выражений:

  • Типы значений: этот аргумент вычисляется немедленно (страстная оценка) при вызове функции, а результирующее значение передается в функцию.
    • AnyVal: принимает скаляр или таблицу. Это значение по умолчанию, если не указан тип параметра.
    • Scalar: принимает скалярное значение (может дополнительно добавить подтип).
    • Table: принимает таблицу.
  • Типы выражений: этот аргумент передает невычисленное выражение (отложенное вычисление). Функция решает, когда и в каком контексте его оценивать. Это необходимо для ссылочных параметров и полезно, если необходимо управлять контекстом фильтра (например, внутри CALCULATE). expr типы могут быть ссылками на столбец, таблицу, календарь или меру.
    • AnyRef: принимает любую ссылку. Это эквивалентно не указанию типа выражения.
    • CalendarRef: принимает ссылку на календарь.
    • ColumnRef: принимает ссылку на столбец.
    • MeasureRef: принимает ссылку на меру.
    • TableRef: принимает ссылку на таблицу.

Типы значений (AnyVal, Scalar, Table) поддерживают неявное приведение типов. Типы выражений (AnyRef, CalendarRef, ColumnRef, MeasureRef, TableRef) отсутствуют.

Подтип

Подтип позволяет определить определенный Scalar тип данных. Если вы определяете подтип, не нужно явно определять параметр как Scalar тип, это автоматически предполагается.

Подтипы:

  • Variant: принимает любой скаляр.
  • Int64: принимает целое число.
  • Decimal: принимает десятичное значение фиксированной точности (например, валюта или деньги).
  • Double: принимает десятичную запятую с плавающей запятой.
  • String: принимает текст.
  • DateTime: принимает дату и время.
  • Boolean: принимает TRUE/FALSE.
  • Numeric: принимает любое числовое значение (Int64или DecimalDouble подтипы)

Режим параметра

ParameterMode определяет, когда и где вычисляется выражение параметра. К ним относятся:

  • val (страстная оценка): выражение вычисляется один раз перед вызовом функции. Полученное значение затем передается в функцию. Это обычно для простых скалярных или табличных входных данных. Это значение по умолчанию, если вы не указываете parameterMode для параметра.
  • expr (отложенное вычисление): выражение вычисляется внутри функции, потенциально в другом контексте (например, в контексте строки или фильтра) и может вычисляться несколько раз, если оно упоминается несколько раз или находится в цикле. Это необходимо для ссылочных параметров и полезно, если необходимо контролировать контекст оценки.

Тип Scalar может использовать либо val либо expr. Используйте val, если вы хотите, чтобы скаляр вычислялся один раз в контексте вызывающего. Используйте expr , когда требуется отложить оценку и, возможно, применить контекст внутри функции. См. пример: параметр таблицы в качестве примера.

Тип выражения (AnyRef, ColumnRef и т. д.) должен быть expr, потому что его ссылки (столбцы, таблицы, меры и т. д.) должны быть оценены в контексте функции.

В следующей таблице перечислены эффективные и разрешенные параметры режима.

Тип ParameterMode не указан ParameterMode: val ParameterMode: expr
(не указано) / AnyVal val val expr
Scalar, Table val val expr
AnyRef expr Не разрешенный expr
CalendarRef, ColumnRef, MeasureRef, TableRef expr Не разрешенный expr

Пример: приведение типов

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

В этом случае используется Scalar тип, Int64 подтип и val режим параметра для прогнозируемого округления и приведения текста к числу, а также для обеспечения быстрой оценки любых выражений. Это также можно сделать, добавив Int64 подтип, как показано в приведенном ниже примере. Нечисловые строки приводят к ошибке.

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

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

Пример: параметр таблицы (значение и выражение)

Чтобы проиллюстрировать, как UDF parameterMode влияет на контекст фильтра, рассмотрите две функции, которые считают строки в таблице Sales. Оба содержат CALCULATETABLE(t, ALL('Date')) в своих телах, но один параметр объявлен как val (нетерпеливая оценка), а другой как expr (отложенная оценка):

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 возвращает количество продаж только за финансовый год 2020. Таблица Sales уже фильтруется по году до вызова функции, поэтому ALL('Date') внутри функции не оказывает эффекта.

CountRowsLater возвращает количество продаж за все годы. Функция получает невычисленное табличное выражение и вычисляет его в ALL('Date'), удаляя фильтр внешнего года.

Проверка типов

Проверку типов в определяемых пользователем функциях можно выполнить с новыми и существующими функциями проверки типов, которые можно вызвать внутри тела функции, чтобы подтвердить тип среды выполнения переданных параметров. Это позволяет UDF использовать управление контекстом, проверять параметры заранее, нормализовать входные данные перед вычислением.

Замечание

Для параметров expr, находящихся в режиме parameterMode, проверка типов выполняется, когда параметр упоминается в теле функции (а не во время вызова функции).

Доступные функции проверки типов

Пользовательские функции (UDFs) могут использовать функции для проверки скалярных значений. Каждое возвращаемое значение TRUE/FALSE зависит от того, является ли указанное значение этим типом.

Категория Functions
Numeric ISNUMERIC, ISNUMBER
Double ISDOUBLE
Целое число ISINT64, ISINTEGER
Decimal ISDECIMAL, ISCURRENCY
String ISSTRING, ISTEXT
Boolean ISBOOLEAN, ISLOGICAL
Дата и время ISDATETIME

Пример. Проверка того, является ли параметр строкой

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

Это предотвращает ошибки и позволяет решить, как обрабатывать нестроковые входные данные в функцию (в этом примере возвращается BLANK).

Пример. Принятие нескольких типов параметров

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"

В этом примере показано, как использовать проверку типов в пользовательских функциях для безопасного приема нескольких типов входных данных и возвращения предсказуемого единого результата. GetCurrencyName принимает один аргумент, который может быть либо ключом валюты целого числа, currencyлибо текстовым кодом валюты. Функция проверяет тип аргумента с помощью ISINT64. Если входные данные являются целым числом, он вызывает вспомогательный метод GetCurrencyNameByKey , который ищет имя валюты на основе ключа валюты. Если входные данные не являются целым числом, он вызывает вспомогательный метод GetCurrencyNameByCode , который ищет имя валюты на основе кода валюты.

Полезные информационные функции

При создании определяемых пользователем функций полезны следующие информационные функции:

  • TABLEOF: возвращает полную таблицу, связанную с заданным столбцом, мерой или календарем.
  • NAMEOF: возвращает имя таблицы, столбца, меры или календаря в виде текстовой строки.

Пример: функция MODEX

В следующем примере показана базовая реализация функции MODEX, которая возвращает наиболее часто возникающие значения выражения, вычисляемого по таблице. Программа использует TABLEOF для автоматического определения правильной таблицы по указанной ссылке.

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] )

Определение нескольких функций одновременно

Определяемые пользователем функции (UDF) позволяют определять несколько функций в одном запросе или сценарии, что упрощает и делает более удобной организацию повторно используемой логики. Это особенно полезно, если вы хотите инкапсулировать связанные вычисления или вспомогательные подпрограммы вместе. Функции можно оценивать вместе или по отдельности.

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

Расширенный пример: гибкое преобразование валют

Чтобы продемонстрировать, как DAX определяемые пользователем функции способны обрабатывать более сложную логику, мы рассмотрим сценарий конвертации валюты. В этом примере используются проверки типов и вложенные функции для преобразования заданной суммы в целевую валюту с использованием среднего или котировочного курса на конец дня для указанной даты.

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

Функция ConvertToCurrency принимает гибкие типы входных данных для валюты и даты. Пользователи могут предоставить ключ валюты или ключ даты напрямую или указать код валюты или стандартное значение даты. Функция проверяет тип каждого входного и обрабатывает его соответствующим образом: если pCurrency имеется целое число, оно обрабатывается как ключ валюты. В противном случае функция предполагает код валюты и пытается устранить соответствующий ключ. pDate следует аналогичному шаблону, если это целое число, он рассматривается как ключ даты; в противном случае функция предполагает, что это стандартное значение даты и преобразуется в ключ даты с помощью вспомогательной ConvertDateToDateKey функции. Если функция не может определить допустимый обменный курс, она возвращает сообщение "обменный курс недоступен".

Затем эту логику можно использовать для определения такой меры, как Total Sales in Local Currency.

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

Это можно при необходимости связать со строкой динамического формата , чтобы отобразить результат в соответствующем формате валюты.

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

Пример результата можно увидеть на снимке экрана ниже.

Таблица с полной датой, валютой, суммой продаж в локальной валюте и общими продажами.

Соображения и ограничения

Определяемые пользователем функции в настоящее время находятся в предварительной версии и во время предварительной версии, помните о следующих рекомендациях и ограничениях:

Общие сведения:

  • Не удается создавать или моделировать DAX определяемые пользователем функции в сервисе.

  • Невозможно скрыть или отменить скрытие UDF в модели.

  • Не удается поместить определяемые пользователем функции в папки отображения.

  • Кнопка "Создать функцию" на ленте отсутствует.

  • Невозможно объединить UDF (определяемые пользователем функции) с переводами.

  • ПФО не поддерживаются в моделях без таблиц.

  • Безопасность на уровне объектов (OLS) не передается в функции или наоборот. Например, рассмотрим следующую функцию F , которая относится к защищенной мере MyMeasure:

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

    MyMeasure если защита обеспечивается с помощью безопасности на уровне объектов, функция F не защищена автоматически. Если F выполняется под удостоверением без доступа к MyMeasure, он действует так, как будто MyMeasure не существует. Рекомендуется избегать выявления безопасных объектов в именах и описаниях функций.

  • Поддерживается коррекция формул и вычисление зависимостей, с известным ограничением, связанным с неквалифицированными именами. Так как неквалифицированные имена интерпретируются как ссылки на измерения, их нельзя надежно исправить или включить в отслеживание зависимостей, если они предназначены для указания на столбцы. Неквалифицированное имя — это ссылка на объект, не включающая префикс таблицы.

  • CalendarRef ColumnRef, MeasureRefи TableRef указания типов могут не приниматься во всех вызовах функций во время предварительной версии. Пользователь может вернуться к AnyRef.

Определение UDF:

  • Рекурсия или взаимная рекурсия не поддерживается.
  • Перегрузка функций не поддерживается.
  • Явные типы возвращаемых данных не поддерживаются.

Параметры UDF:

  • Необязательные параметры не поддерживаются.
  • Описания параметров не поддерживаются.
  • ПФП не могут возвращать значение enum. Встроенные функции, которые принимают enum значения в качестве параметров функции, не смогут использовать ПФП в этом контексте.
  • Параметры указания типа expr, которые являются несвязанными, не обрабатываются.

Поддержка IntelliSense:

  • Хотя пользовательские функции можно использовать в динамических или составных моделях, поддержка IntelliSense отсутствует.
  • Хотя определяемые пользователем функции можно использовать в визуальных вычислениях, строка формул визуальных вычислений не поддерживает IntelliSense для определяемых пользователем функций.
  • Представление TMDL имеет ограниченную поддержку IntelliSense для определяемых пользователем функций.

Известные ошибки

Следующие проблемы в настоящее время известны и могут повлиять на функциональные возможности:

  • Некоторые расширенные сценарии, связанные с определяемыми пользователем функциями, могут привести к несоответствиям синтаксического анализа. Например, пользователи могут видеть красные подчеркивания или ошибки валидации при передаче столбцов в качестве expr параметров или использовании неквалифицированных ссылок на столбцы.