在Databricks / Spark中的SQL中为变量分配动态值

eufgjt7s  于 2023-10-23  发布在  Apache
关注(0)|答案(6)|浏览(170)

我觉得我一定错过了一些明显的东西,但我似乎不能在Spark SQL中动态设置变量值。
假设我有两个表,tableSrctableBuilder,我正在创建tableDest
我一直在尝试

SET myVar FLOAT = NULL

SELECT
    myVar = avg(myCol)
FROM tableSrc;

CREATE TABLE tableDest(
    refKey INT,
    derivedValue FLOAT
);

INSERT INTO tableDest
    SELECT
        refKey,
        neededValue * myVar AS `derivedValue`
    FROM tableBuilder

在T-SQL中这样做是微不足道的,微软(DECLARE... SELECT)出人意料地获胜。
Error in SQL statement: ParseException: mismatched input 'SELECT' expecting <EOF>(line 53, pos 0)
但是我似乎不能将派生值赋给变量以供重用。我尝试了一些变体,但最接近的是将变量赋给select语句的字符串。

请注意,这是从T-SQL中的一个功能齐全的脚本改编的,所以我不会很快将十几个SQL变量拆分出来,用Python spark查询来计算所有这些变量,只是为了在几百行的f字符串中插入{var1}{var2}等。更糟糕的是,如果可能的话,我想避免这种情况。

plicqrtu

plicqrtu1#

使用的SET命令用于spark.conf get/set,而不是SQL查询的变量
对于SQL查询,您应该使用小部件:
https://docs.databricks.com/notebooks/widgets.html
但是,有一种方法可以在SQL上使用spark.conf参数:
%python spark.conf.set('personal.foo','bar')
然后您可以使用:用途:
$sql select * from table where column = '${personal.foo}';
技巧部分是您必须在spark.conf的名称上使用一个“点”(或其他特殊字符),否则SQL单元将期望您在运行时为$变量提供值(在我看来这是一个bug,我相信用{}舍入应该足够了)

b91juud3

b91juud32#

Databricks刚刚发布了SQL user defined functions,它可以处理类似的问题,而不会带来性能损失,对于您的示例,它看起来如下所示:

CREATE TEMP FUNCTION myVar()
RETURNS FLOAT
LANGUAGE SQL
RETURN 
SELECT
    avg(myCol)
FROM tableSrc;

然后是用途:

SELECT
      refKey,
      neededValue * myVar() AS `derivedValue`
FROM tableBuilder
svujldwt

svujldwt3#

我已经围绕这个问题很长一段时间。最后,我找到了一个使用@Ronieri Marques解决方案和一些pyspark函数的解决方案。我将尝试在下面提供完整的工作代码:
首先创建一个示例表:

%sql
create table if not exists calendar
as 
select '2021-01-01' as date
union
select '2021-01-02' as date
union
select '2021-01-03' as date

%sql 
-- just to show the max and min dates
select max(date), min(date) from calendar

结合sqlContext + toJSON,可以动态地为变量赋值,在这种情况下,我使用查询:

%python
result = sqlContext.sql("select max(date), min(date) from calendar").toJSON()
spark.conf.set('date.end'    , result.first()[14:24])
spark.conf.set('date.start'  , result.first()[39:49])

最后,可以在SQL查询中使用变量:

%sql 
select * from calendar where date > '${date.start}' and date < '${date.end}'

注意,子字符串result.first()[14:24]result.first()[39:49]是必需的,因为result.first()的值是*{“max(date)":“2021-01-03”,“min(date)":“2021-01-01”}*,所以我们需要“定制”最终结果,只选取我们需要的值。
也许代码可以被润色,但现在它是我唯一能实现的工作解决方案。
我希望这个解决方案能对某人有用。

iqxoj9l9

iqxoj9l94#

Databricks现在也有SQL小部件https://docs.databricks.com/notebooks/widgets.html#widgets-in-sql

CREATE WIDGET TEXT p_file_date DEFAULT "2021-03-21";
Select * from results where results.file_date = getArgument("p_file_date")
n8ghc7c1

n8ghc7c15#

在变量赋值的末尾缺少一个分号。

SET myVar FLOAT = NULL;
...

希望有帮助:)

esyap4oy

esyap4oy6#

现在支持declare和set语法,但仅在databricks runtime 14.1及更高版本上支持

相关问题