|
|
# 结构化 SQL 生成器
|
|
|
|
|
|
> 用 JSON 来轻松生成复杂的 SQL,大幅提高写 SQL 的效率!
|
|
|
>
|
|
|
> by 程序员鱼皮
|
|
|
|
|
|
在线使用:http://sql.yupi.icu
|
|
|
|
|
|
![工具截图](./doc/assets/main.png)
|
|
|
|
|
|
## 项目作用
|
|
|
|
|
|
1. 将 SQL 的编写逻辑 `结构化` ,像写文章大纲一样编写和阅读 SQL
|
|
|
2. 重复的 SQL 只需编写一次 ,SQL 变动时修改一处即可
|
|
|
3. 可以针对某部分 SQL 进行传参和调试
|
|
|
4. 查看 SQL 语句的引用树和替换过程,便于分析理解 SQL
|
|
|
|
|
|
![查看调用树和替换过程](./doc/assets/invokeTree.png)
|
|
|
|
|
|
## 应用场景
|
|
|
|
|
|
如果你要写一句复杂的 SQL,且 SQL 中很多代码是 **相似** 但又不相同的。那么推荐使用该工具,可以不用重复编写 SQL,更有利于修改、维护和理解。
|
|
|
|
|
|
尤其是在大数据分析场景下,经常会有编写复杂 SQL 的需求。
|
|
|
|
|
|
> 之所以会有这个轮子,也正是因为鱼皮在工作中要写一句长达 3000 行的 SQL 来离线分析数据,手写真的人要疯了!
|
|
|
|
|
|
当然,你也完全可以把它当做一个 `重复代码生成器` ~
|
|
|
|
|
|
## 示例
|
|
|
|
|
|
需求:计算 id = 1 和 id = 2 的两位同学的身高差
|
|
|
|
|
|
SQL 大概是这样的:
|
|
|
|
|
|
```sql
|
|
|
select (s1.height - s2.height) as 身高差
|
|
|
from
|
|
|
(select * from student where id = 1) s1,
|
|
|
(select * from student where id = 2) s2
|
|
|
```
|
|
|
|
|
|
显然,上述 SQL 中学生表查询了 2 次,而且除了查询的 id 不同外,查询逻辑完全一致!
|
|
|
|
|
|
如果后面查询学生的逻辑发生修改,那么以上 2 个子查询都要同时修改,不利于维护。
|
|
|
|
|
|
而使用本工具,只需编写如下 JSON,就能自动生成完整的 SQL 了:
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"main": "select @身高差() from (@学生表(id = 1)) s1, (@学生表(id = 2)) s2",
|
|
|
"身高差": "(s1.height - s2.height) as 身高差",
|
|
|
"学生表": "select * from student where id = #{id}"
|
|
|
}
|
|
|
```
|
|
|
|
|
|
通过类似 `函数调用 + 传参` 的方式,我们无需重复编写 SQL,而且整个 SQL 的逻辑更清晰!
|
|
|
|
|
|
当然,以上只是一个示例,真实大数据离线分析的场景下,SQL 可比这复杂 N 倍!
|
|
|
|
|
|
## 优势
|
|
|
|
|
|
1. 支持在线编辑 JSON 和 SQL,支持代码高亮、语法校验、一键格式化、查找和替换、代码块折叠等,体验良好
|
|
|
2. 支持一键生成 SQL
|
|
|
3. 支持参数透传,比如 @a(xx = #{yy}),yy 变量可传递给 @a 公式
|
|
|
4. 支持嵌套传参(将子查询作为参数),比如 @a(xx = @b(yy = 1))
|
|
|
5. 不限制用户在 JSON 中编写的内容,因此该工具也可以作为重复代码生成器来使用
|
|
|
6. 支持查看 SQL 语句的调用树和替换详情,便于分析引用关系
|
|
|
|
|
|
## 文档
|
|
|
|
|
|
可以把下面的代码放到生成器中试试,一下就明白如何使用啦~
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"main": "必填, 代码从这里开始生成, 用 @规则名() 引用其他语句",
|
|
|
"规则名": "可以编写任意 SQL 语句 @规则名2() @动态传参(a = 求给 ||| b = star)",
|
|
|
"规则名2": {
|
|
|
"sql": "用 #{参数名} 指定可被替换的值",
|
|
|
"params": {
|
|
|
"参数名": "在 params 中指定静态参数, 会优先被替换"
|
|
|
}
|
|
|
},
|
|
|
"动态传参": "#{a}鱼皮#{b}"
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### 补充说明
|
|
|
|
|
|
`对象键`:定义 SQL 生成规则名称,main 表示入口 SQL,从该 SQL 语句开始生成。
|
|
|
|
|
|
`对象值`:定义具体生成规则。可以是 SQL 字符串或者对象。
|
|
|
|
|
|
`sql`:定义模板 SQL 语句,可以是任意字符串,比如一组字段、一段查询条件、一段计算逻辑、完整 SQL 等。
|
|
|
|
|
|
`params`:静态参数,解析器会优先将该变量替换到当前语句的 #{变量名} 中
|
|
|
|
|
|
`#{xxx}`:定义可被替换的变量,优先用当前层级 params 替换,否则由外层传递
|
|
|
|
|
|
`@xxx(yy = 1 ||| zz = #{变量})`:引用其他 SQL,可传参,参数可再用变量来表示,使用 |||(三个竖线)来分隔参数。
|
|
|
|
|
|
|
|
|
## 实现
|
|
|
|
|
|
使用和 JSON 相性最好的 JavaScript 来实现,编写一份逻辑 JS 文件,可同时应用于 browser 和 server 端。
|
|
|
|
|
|
功能比较轻量,因此选择优先在纯 browser 端实现。
|
|
|
|
|
|
前端使用 `Vue3 + Vite + Ant Design Vue` 开发界面,选用 `Monaco Editor` 实现代码编辑、高亮、格式化等功能。
|
|
|
|
|
|
SQL 生成逻辑如下:
|
|
|
|
|
|
1. JSON 字符串转对象
|
|
|
2. 从入口开始,先替换 params 静态参数,得到当前层解析
|
|
|
3. 对 @xxx 语法进行递归解析,递归解析时,优先替换静态参数,再替换外层传来的调用参数
|
|
|
4. 得到最终 SQL
|
|
|
|
|
|
解析器原本采用正则非贪婪替换方式实现,但无法实现嵌套调用,比如 @a(xx = @b()),会被识别为 @a(xx = @b(),匹配到了最近的右括号。
|
|
|
因此针对括号嵌套的情况对子查询替换算法做了优化,已支持包含括号语句的嵌套调用。
|
|
|
|