Поделиться через


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

В этой статье приведены примеры определяемых пользователем функций Scala. Здесь показано, как регистрировать и вызывать такие функции, а также даны рекомендации, касающиеся порядка вычислений частей выражений в Spark SQL. Дополнительные сведения см. в статье о внешних скалярных функциях (ОПРЕДЕЛяемых пользователем) функциях.

Примечание.

Для вычислительных ресурсов с поддержкой каталога Unity scala UDFs с режимом общего доступа требуется Databricks Runtime 14.2 и более поздних версий.

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

val squared = (s: Long) => {
  s * s
}
spark.udf.register("square", squared)

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

spark.range(1, 20).createOrReplaceTempView("test")
%sql select id, square(id) as id_squared from test

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

import org.apache.spark.sql.functions.{col, udf}
val squared = udf((s: Long) => s * s)
display(spark.range(1, 20).select(squared(col("id")) as "id_squared"))

Порядок вычисления и проверка значений NULL

В SQL Spark (включая SQL, а также API кадров и наборов данных) не гарантируется определенный порядок вычисления частей выражения. В частности, входные данные оператора или функции не обязательно вычисляются слева направо или в любом другом фиксированном порядке. Например, логические выражения AND и OR не имеют привычной семантики слева направо.

Таким образом, не следует полагаться на побочные эффекты или порядок вычисления логических выражений, а также порядок предложений WHERE и HAVING, так как этот порядок и правила применения предложений могут изменяться в результате оптимизации или планирования запросов. В частности, если определяемая пользователем функция использует привычную семантику в SQL для проверки значений NULL, нет гарантии того, что эта проверка произойдет перед вызовом функции. Например,

spark.udf.register("strlen", (s: String) => s.length)
spark.sql("select s from test1 where s is not null and strlen(s) > 1") // no guarantee

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

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

  • Реализуйте в функции поддержку значения NULL и выполняйте проверку этих значений внутри самой функции.
  • Используйте выражения IF или CASE WHEN для проверки значения NULL и вызова определяемой пользователем функции в условной ветви.
spark.udf.register("strlen_nullsafe", (s: String) => if (s != null) s.length else -1)
spark.sql("select s from test1 where s is not null and strlen_nullsafe(s) > 1") // ok
spark.sql("select s from test1 where if(s is not null, strlen(s), null) > 1")   // ok

API типизированного набора данных

Примечание.

Эта функция поддерживается в кластерах с поддержкой каталога Unity с режимом общего доступа в Databricks Runtime 15.4 и выше.

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

Например, следующее приложение Scala использует map() API для изменения числа в столбце результатов на префиксированную строку.

spark.range(3).map(f => s"row-$f").show()

Хотя в этом примере используется map() API, это также относится к другим типизированным API набора данных, таким как filter(), , foreach()mapPartitions(), reduce()foreachPartition()и flatMap().