【问题标题】:Performance of SQL UDF's vs Javascript UDF's in SnowFlakeSnowFlake 中 SQL UDF 与 Javascript UDF 的性能对比
【发布时间】:2018-05-21 08:38:07
【问题描述】:

我们正在尝试在 Snowflake 中实现间隔值的添加。由于 Snowflake 中不支持间隔数据类型,我们正在尝试使用 UDF 来实现相同的功能。我们将间隔转换为秒,在得到总秒数后,我们将总秒转换为所需的目标间隔类型。

我们使用 SQL UDF 和 Javascript UDF 实现了相同的逻辑。

Javascript UDF

    create or replace function js_interval(interval_1 string, interval_sign_1 double, interval_2 string, interval_sign_2 double)
  returns string
  language javascript
  strict
  as '     
  if ((INTERVAL_1.substring(0,1)=="-"))
  {
    var res = INTERVAL_1.split(":");
    var res2=(res[1]*-60+res[0]*3600);
    var res3= -1*res[2];
  }
  else
  {
    var res = INTERVAL_1.split(":");
    var res2=(res[1]*60+res[0]*3600);
    var res3= res[2];
  }
  if ((INTERVAL_2.substring(0,1)=="-"))
  {
     var res11 = INTERVAL_2.split(":");
     var res22=(res11[1]*-60+res11[0]*3600);
     var res33= -1*res11[2];
  }
  else
  {
     var res11 = INTERVAL_2.split(":");
     var res22=(res11[1]*60+res11[0]*3600);
     var res33= res11[2];
  }
  if(INTERVAL_SIGN_1 > 0)
  { 
    var result1= res2*1+res3*1;
  }
  else
  {
    var result1=(res2*1+res3*1)*-1;
  }
  if(INTERVAL_SIGN_2 > 0)
  { 
    var result2= res22*1+res33*1;
  }
  else
  {
    var result2=(res22*1+res33*1)*-1;
  }
  var final_sec= result1+result2;

  if (final_sec < 0 )
  {
    return "-"+String(Math.trunc(Math.abs(final_sec)/3600))+":"+String(Math.trunc(Math.abs(final_sec)/60)%60)+":"+String(Math.abs(final_sec)%60);
  }    
  else
  {
    return String(Math.trunc(Math.abs(final_sec)/3600))+":"+String(Math.trunc(Math.abs(final_sec)/60)%60)+":"+String(Math.abs(final_sec)%60);
  }
';

SQL UDF

 create or replace function addIntervalsH2S ( interval_1 varchar, interval_type_1 varchar , interval_sign_1 int, interval_2 varchar, interval_type_2 varchar, interval_sign_2 int )
  returns varchar 
  as
  'select trunc((((split_part(interval_1,\':\',1)*3600+split_part(interval_1,\':\',2)*60+split_part(interval_1,\':\',3))*interval_sign_1)+((split_part(interval_2,\':\',1)*3600+split_part(interval_2,\':\',2)*60+split_part(interval_2,\':\',3))*interval_sign_2))/3600)||\':\'||mod(trunc((((split_part(interval_1,\':\',1)*3600+split_part(interval_1,\':\',2)*60+split_part(interval_1,\':\',3))*interval_sign_1)+((split_part(interval_2,\':\',1)*3600+split_part(interval_2,\':\',2)*60+split_part(interval_2,\':\',3))*interval_sign_2))/60),60)||\':\'||mod((((split_part(interval_1,\':\',1)*3600+split_part(interval_1,\':\',2)*60+split_part(interval_1,\':\',3))*interval_sign_1)+((split_part(interval_2,\':\',1)*3600+split_part(interval_2,\':\',2)*60+split_part(interval_2,\':\',3))*interval_sign_2)),60)
  end';                

基于 Javascript 的 UDF 用于嵌套级别间隔加法查询的性能远优于基于 SQL 的 UDF。

造成巨大性能差异的原因可能是什么?

【问题讨论】:

  • 如果不了解更多信息,很难回答。什么是“更好”的性能?您期望的逻辑到底是什么,例如你可以有超过 24 小时的间隔吗?一般来说,您的 SQL 逻辑似乎只是执行更多操作。优化器可能会共享多次使用的函数的结果,但很难确定。

标签: user-defined-functions snowflake-cloud-data-platform


【解决方案1】:

我们发现 SQL 中的子查询存在问题,即使在简单查询中,例如

SELECT (SELECT expression1) + (SELECT expression2)

两个子查询都被评估为单独的执行“步骤”(除非expression1/2 非常简单)。每个“步骤”都像是一个完整的单独查询,因此速度很慢。

由于 SQL UDF 在内部展开为子查询,这会导致每次调用以 SELECT 开头的 SQL UDF 都会使用额外的步骤进行评估,从而显着降低速度。

解决方案?尝试从 SQL UDF 的开头删除 SELECT 关键字,这应该会更快。这类似于将上述查询重写为

SELECT (expression1) + (expression2)

【讨论】:

    猜你喜欢
    • 2012-01-11
    • 2020-03-17
    • 1970-01-01
    • 1970-01-01
    • 2016-11-12
    • 1970-01-01
    • 2010-11-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多