Функция Expression.Evaluate() в Power Query/M

Текст представляет собой адаптированный перевод статьи Chris Webb (Крис Вебб), оригинал – Expression.Evaluate() In Power Query/M. Рассматривается англоязычный Power Query.

Об авторе

Крис Вебб (Chris Webb) — независимый эксперт, консультант по технологиям Analysis Services, MDX, Power Pivot, DAX, Power Query и Power BI. Его блог — это кладезь информации на тему перечисленных технологий. Вот уже более 10 лет он пишет про BI-решения от Microsoft. Количество его статей перевалило за 1000! Также Крис выступает на большом количестве различных конференций вроде SQLBits, PASS Summit, PASS BA Conference, SQL Saturdays и участвует в различных сообществах.
Крис любезно разрешил нам переводить его статьи на русский язык. И это одна из них.

Функция Expression.Evaluate() в Power Query/M

Некоторое время назад была написана статья, где описывается загрузка М кода из текстового файла с помощью функции Expression.Evaluate(). Сегодня мы рассмотрим эту функцию более подробно и добавим несколько примеров к тем, что есть на официальном сайте Майкрософт.

Документация даёт ясный ответ, что делает Expression.Evaluate: берёт выражение на языке М, вычисляет и возвращает результат. Тут важно понимать, что выражение на языке М может быть больше, чем строка кода. Фактически, запрос Power Query является одним выражением.

Вот простой пример использования Expression.Evaluate():

let
    Source = Expression.Evaluate("""Hello World""")
in
    Source

В результате получаем текст «Hello World»:

p1.png

А вот другой пример, возвращающий число 10:

let
    Source = Expression.Evaluate("6+4")
in
    Source

p2.png

Помните, что запрос Power Query это одно выражение? Достигается это через использование оператора let. В результате одно выражение может состоять из многих подвыражений.

Вот пример с оператором let, возвращающий 12:

let
    Source = Expression.Evaluate("let a=3, b=4, c=a*b in c")
in
    Source  

p3.png

Хорошо, пока не очень интересно. Что нам действительно надо, это вычисление сложных выражений.

Рассмотрим следующий запрос, который использует функцию Text.Upper() и возвращает текст «ABC»:

let
    Source = Text.Upper("abc")
in
    Source

Но если мы попытаемся выполнить следующий запрос, то получим ошибку: Имя «Text.Upper» не существует в текущем контексте.

let
    Source = Expression.Evaluate("Text.Upper(""abc"")")
in
    Source  

p4.png

Для полного понимания того, что происходит, мы рекомендуем ознакомиться с разделом «3.3 Environments and Variables» спецификации языка. Краткое объяснение в том, что все выражения вычисляются в «среде окружения», где существуют другие переменные, на которые можно ссылаться.
Причина, по которой запрос выдал ошибку в том, что вычисления происходят в его собственной среде, где глобальная библиотека функций недоступна. Чтобы это исправить, подключим глобальную среду, указав вторым параметром предопределённую переменную #shared:

let
    Source = Expression.Evaluate("Text.Upper(""abc"")", #shared)
in
    Source

p5.png

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

let
    Source = #shared
in
    Source  

Вот пример того, что возвращает данный запрос из книги, содержащей несколько запросов:

p6.png

Применение #shared позволяет вычислять выражения, использующие глобальную библиотеку функций. Но это не волшебная палочка, убирающая все ошибки. В следующем запросе объявляется список и делается попытка получить второй элемент с помощью Expression.Evaluate():

let
    MyList = {1,2,3,4,5},
    GetSecondNumber = Expression.Evaluate("MyList{1}", #shared)
in
    GetSecondNumber

p7.png

Несмотря на использование #shared во втором параметре, мы получили ошибку, т.к. переменная MyList остаётся недоступной. Для исправления этого, определим вторым параметром Expression.Evaluate() запись, чтобы среда окружения знала на что ссылается переменная MyList:

let
    MyList = {1,2,3,4,5},
    GetSecondNumber = Expression.Evaluate("MyList{1}", [MyList=MyList])
in
    GetSecondNumber  

p8.png

Вот пример посложнее. Надеемся, он поможет лучше понять логику построения запроса:

let
    MyList_Outer = {1,2,3,4,5},
    NumberToGet_Outer = 3,
    GetNthNumber = Expression.Evaluate("MyList_Inner{NumberToGet_Inner}",
       [MyList_Inner=MyList_Outer, NumberToGet_Inner=NumberToGet_Outer ])
in
    GetNthNumber

p9.png

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

Нам нужно создать запись, содержащую все имена #shared плюс те, что мы хотим передать дополнительно. В этом нам поможет Record.Combine(). В результате мы получим объединённую запись нашей собственной записи и записи #shared:

let
    MyList_Outer = {1,2,3,4,5},
    NumberToGet_Outer = 1,
    RecordOfVariables =
     [MyList_Inner=MyList_Outer, NumberToGet_Inner=NumberToGet_Outer ],
    RecordOfVariablesAndGlobals = Record.Combine({RecordOfVariables, #shared}),
    GetNthNumber = Expression.Evaluate(
     "List.Reverse(MyList_Inner){NumberToGet_Inner}",
     RecordOfVariablesAndGlobals )
in
    GetNthNumber

p10.png

Следующая статья
Предыдущая статья

Комментирование закрыто.

Идет набор на курс “Power BI для интернет-маркетинга”

Набор на новый поток курса по Power BI

Подпишитесь на нашу рассылку, чтобы не пропустить новые статьи!

Подписка на рассылку NeedForData

Бесплатная шпаргалка по визуализации
Плакат Как что визуализировать
Сертификаты студентов курсов NeedForData.ru

© 2018    NeedForData