前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Byzer 条件分支语法入门

Byzer 条件分支语法入门

作者头像
用户2936994
发布2022-09-28 20:35:25
3940
发布2022-09-28 20:35:25
举报
文章被收录于专栏:祝威廉

SQL 中很缺乏条件分支能力。为此,一般大家都会通过如下两种方式解决:

  1. 模板解决,但一般模板的缺陷是难以和SQL进行动态交互,比如根据上一条SQL语句的结果(比如条数)来决定后续的SQL语句执行逻辑。Byzer 具有模板编程的能力,不过不支持跨语句: Byzer Man:Byzer 模板编程入门
  2. 依托第三方语言,比如 Shell, Python, Java 中调用。不过这样其实门槛就瞬间变高了,使用复杂度和基础设施的要求也变得高了很多。

Byzer 在语言层面支持条件分支。

语法初见

下面是一段典型的分支语句:

代码语言:javascript
复制
set a = "wow,jack";
!if ''' split(:a,",")[0] == "jack" ''';
   select 1 as a as b;
!else;
   select 2 as a as b;
!fi;

select * from b as output;

根据 a 变量的变化, 我们会执行不同的语句,导致 b 表结果发生变化。

在 Byzer 中,if/else 并非关键字,都是一个宏函数。既然是一个宏函数,那么为了符合 Byzer 语法规范,那么每个命令后面都需要添加';'表示这个命令的结束。

我们来细细看看前面的 if 语句:

!if 命令后面接一个文本参数,该文本的内容是一个表达式。在表达式里,我们可以使用大部分SQL支持的函数。比如上面的例子是split函数。 我们使用":"来标识一个变量。变量来源于set语法。比如示例中表达式的:a变量对应的值为"wow,jack",他是通过set语法来设置的。

从上面的例子可以看到,Byzer 的条件表达式语句具有以下特色:

  1. 语法设计遵循SQL的一些原则。比如采用 and/or 替代 &&,||.使用select语句做变量赋值
  2. 兼容大部分SQL函数
  3. 支持多个语句,最后一条语句作为最后的条件
  4. 支持用户自定义函数(参看文章后半部分)

一个复杂的例子

下面的 Byzer 代码示例具有复杂的分支嵌套以及更加复杂的条件表达式:

代码语言:javascript
复制
set a="jack,2";

!if ''' select split(:a,",")[0] as :name, split(:a,",")[1] as :num;
:name == "jack" and :num == 3
''';
    select 0 as a as b;
!elif ''' select split(:a,",")[1] as :num; :num==2 ''';
    !if ''' 2==1 ''';
       select 1.1 as a as b;
    !else;
       select 1.2 as a as b;
    !fi;
!else;
  select 2 as a as b;
!fi;


select * from b as output;

我们再来看 !if 语句

代码语言:javascript
复制
!if ''' select split(:a,",")[0] as :name, split(:a,",")[1] as :num;
:name == "jack" and :num == 3
''';

和上个例子有些不同,因为我们要对:a变量进行多次处理,为了使得最后的表达式更加简单,Byzer 支持通过select语法来做变量赋值。语句

代码语言:javascript
复制
select split(:a,",")[0] as :name, split(:a,",")[1] as :num;

通过该语句,我们得到了:name 和:num两个变量。之后通过";"表示该语句的结束。在后续的语句中我们就可以引用对应的变量了:

代码语言:javascript
复制
:name == "jack" and :num == 3

Byzer 会对:num自动进行类型转换。

一个实际场景

比如我们加载了张表,然后我们通过 b_count 获得该表的数据量,然后根据数据量决定后续的逻辑,此时代码就可以写成下面这个样子:

代码语言:javascript
复制
select 1 as a as mockTable;
set b_count=`select count(*) from mockTable ` where type="sql" and mode="runtime";

!if ''':b_count > 1 ''';    
    select 1 as a  as final_table;
!else;    
    select 2 as a  as final_table;
!fi;    

select * from final_table as output;

变量的作用域

在if/elif里申明的变量有效范围是整个!if/!fi区间。子if/else语句可以看到上层if/else语句的变量。 比如:

代码语言:javascript
复制
set name = "jack";
!if '''select :name as :newname ;:name == "jack" ''';
    !if ''' :newname == "jack" ''';
       !println '''====1''';
    !else;
       !println '''====2 ''';
    !fi;
!else;
   !println '''=====3''';
!fi;

该语句输出为====1,我们在子if语句中使用了上面的 select产生的:newname变量。

同样的,我们可以在子语句里方便的使用变量:

代码语言:javascript
复制
set name = "jack";
!if '''select concat(:name,"dj") as :newname ;:name == "jack" ''';
    !if ''' :newname == "jackdj" ''';
       !println '''====${newname}''';
       select "${newname}" as a as b;
    !else;
       !println '''====2 ''';
    !fi;
!else;
   !println '''=====3''';
!fi;

select * from b as output;

我们可以在if/else的子语句里,比如select,!println等语句里通过原先的'${}'符号引用!if或者!elif里申明的变量。

结合set 变量

条件分支语句结合强大的set语法,其实可以做很多有意思的事情,比如:

代码语言:javascript
复制
set a = "wow,jack" where type="defaultParam";
!if ''' split(:a,",")[0] == "jack" ''';
   select 1 as a as b;
!else;
   select 2 as a as b;
!fi;

select * from b as output;

此时他的输出会是2。 但是如果你在前面加一句:

代码语言:javascript
复制
set a = "jack,";
set a = "wow,jack" where type="defaultParam";
!if ''' split(:a,",")[0] == "jack" ''';
   select 1 as a as b;
!else;
   select 2 as a as b;
!fi;

select * from b as output;

这个时候会输出1. 也就是用户可以通过添加set变量覆盖已经存在的变量从而控制脚本的执行。

函数定义

另外,Byzer 也支持使用自定义UDF函数,并且在if语句中也是可以使用的。比如:

代码语言:javascript
复制
register ScriptUDF.`` as title where 
lang="scala"
and code='''def apply()={
   "jack"
}'''
and udfType="udf";

!if ''' title() == "jack" ''';
   select 1 as a as b;
!else;
   select 2 as a as b;
!fi;

select * from b as output;

这意味着你可以通过代码获取某个接口的值(或者特定的处理逻辑),来决定脚本的最后执行情况。有木有很强大。

if/else带来的问题

因为在 Byzer 里有session的概念。当一个表被使用过后,系统会自动记住。这样可以很方便的用户进行调试。 但是当使用if/else的时候则会有困惑发生。

我们来看下面这个例子:

代码语言:javascript
复制
!if ''' 2==1 ''';
   select 1 as a as b;
!else;   
!fi;

select * from b as output;

当一次运行的时候,系统会报错,因为没有表b. 如果我们将2==1 修改为 1==1 时,则系统正常运行。 然后我们将1==1 再次修改为2==1 此时系统依然正常运行。 因为系统记住了上次运行的b,所以虽然当前没有执行select语句,但是依然有输出,从而造成错误。解决办法有两个:

  1. 请求参数设置sessionPerRequest,这样每次请求都会保证隔离。
  2. 在脚本里备注set __table_name_cache__ = "false"; 让系统不要记住表名。

第二种方式我们推荐,使用如下:

代码语言:javascript
复制
set __table_name_cache__ = "false";
!if ''' 2==1 ''';
   select 1 as a as b;
!else;   
!fi;

select * from b as output;

指的注意的是,!fi 如果是最后一条语句,不会有任何输出,如果你希望看到某张表的数据,需要显示的在最后加一条 select 语句。

总结

Byzer 通过引入了分支条件语句,结合Byzer Man:Byzer 模板编程入门 以及 Byzer Man:Byzer 模块化编程 ,极大的提升了 Byzer 语言的灵活性和表达能力。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-09-03,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 语法初见
  • 一个复杂的例子
  • 一个实际场景
  • 变量的作用域
  • 结合set 变量
  • 函数定义
  • if/else带来的问题
    • 总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档