@@ -58,6 +58,8 @@ from | |||||
当然,以上只是一个示例,真实大数据离线分析的场景下,SQL 可比这复杂 N 倍! | 当然,以上只是一个示例,真实大数据离线分析的场景下,SQL 可比这复杂 N 倍! | ||||
如果感兴趣的话,欢迎往下看文档,还有更复杂的示例~ | |||||
## 优势 | ## 优势 | ||||
1. 支持在线编辑 JSON 和 SQL,支持代码高亮、语法校验、一键格式化、查找和替换、代码块折叠等,体验良好 | 1. 支持在线编辑 JSON 和 SQL,支持代码高亮、语法校验、一键格式化、查找和替换、代码块折叠等,体验良好 | ||||
@@ -99,6 +101,72 @@ from | |||||
`@xxx(yy = 1 ||| zz = #{变量})`:引用其他 SQL,可传参,参数可再用变量来表示,使用 |||(三个竖线)来分隔参数。 | `@xxx(yy = 1 ||| zz = #{变量})`:引用其他 SQL,可传参,参数可再用变量来表示,使用 |||(三个竖线)来分隔参数。 | ||||
## 复杂示例 | |||||
需求:用一句 SQL 查询出以下表格 | |||||
![](./doc/assets/complex-example.png) | |||||
这个表格的难点在哪? | |||||
1. 查汇总和查明细的粒度不同,不能用 group by 区分,只能用 union(红色) | |||||
2. 分类列中不同行的数据有交叉,不能用 group by 区分,只能用 union( | |||||
3. 每一列由多张表共同 join 而成,且不同分类可关联的表不同,须进行区分(灰色表示无法关联),并将缺失的字段补齐(否则无法 union) | |||||
4. 不同行的同一列计算公式可能不同(蓝色) | |||||
5. 不同列的过滤条件不同(比如最后两列墨绿色是要查全校,其余列只查 1 年级) | |||||
6. 要查询同环比,只能用 2 份完整的数据去 join 然后错位计算来得出 | |||||
显然,这个表中很多查询逻辑是重复但又不同的。 | |||||
这么算下来,最后这个 SQL 中到底会包含多少个基础表的 select 呢?每个基础表查询要重复编写多少遍呢? | |||||
然而,这个表格也只是鱼皮对实际需求简化后才得来的,实际需求比这还复杂几倍! | |||||
可想而知,人工写有多恶心?! | |||||
但是使用本工具,只需编写如下结构化的 JSON: | |||||
```json | |||||
{ | |||||
"main": "select (a / b - 1) from (@查整体(date = 今天)) a, (@查整体(date = 昨天)) b", | |||||
"查整体": "@查年级() union @查1班() union @查2班() where date = #{date}", | |||||
"查年级": "@查汇总_性别汇总() union @查汇总_性别分组() union @查汇总_爱好汇总() union @查汇总_爱好分组() union @查汇总_电脑类别汇总() union @查汇总_电脑类别分组()", | |||||
"查汇总_性别汇总": "@查除电脑关联表()", | |||||
"查汇总_性别分组": "@查除电脑关联表() group by 性别", | |||||
"查汇总_爱好汇总": "@查除电脑关联表()", | |||||
"查汇总_爱好分组": "@查除电脑关联表() where 爱好 in (xx) group by 爱好", | |||||
"查汇总_电脑类别汇总": "@查除三连和学习表()", | |||||
"查汇总_电脑类别分组": "@查除三连和学习表() group by 电脑类别", | |||||
"查1班": "@查1班_性别汇总() union @查1班_性别分组() union @查1班_爱好汇总() union @查1班_爱好分组() union @查1班_电脑类别汇总() union @查汇总_电脑类别分组()", | |||||
"查1班_性别汇总": "@查除电脑关联表() where 1班", | |||||
"查1班_性别分组": "@查除电脑关联表() where 1班 group by 性别", | |||||
"查1班_爱好汇总": "@查除电脑关联表() where 1班", | |||||
"查1班_爱好分组": "@查除电脑关联表() where 1班 and 爱好 in (xx) group by 爱好", | |||||
"查1班_电脑类别汇总": "@查除三连和学习表() where 1班", | |||||
"查1班_电脑类别分组": "@查除三连和学习表() where 1班 group by 电脑类别", | |||||
"查2班": "@查2班_性别汇总() union @查2班_性别分组() union @查2班_电脑类别汇总() union @查2班_电脑类别分组()", | |||||
"查2班_性别汇总": "@查除电脑关联表() where 2班", | |||||
"查2班_性别分组": "@查除电脑关联表() where 2班 group by 性别", | |||||
"查2班_电脑类别汇总": "@查除三连和学习表() where 2班", | |||||
"查2班_电脑类别分组": "@查除三连和学习表() where 2班 group by 电脑类别", | |||||
"查所有关联表": "@查信息表() left join (@查三连表()) left join (@查学习表()) left join (@查电脑表()) left join (@查全校信息())", | |||||
"查除电脑关联表": "@查信息表() left join (@查三连表()) left join (@查学习表()) left join (@查全校信息())", | |||||
"查除三连和学习表": "@查信息表() left join (@查电脑表()) left join (@查全校信息())", | |||||
"查信息表": "select 字段 from 信息表 where 年级 = 1", | |||||
"查三连表": "select 字段 from 三连表 where 年级 = 1", | |||||
"查学习表": "select 字段 from 学习表 where 年级 = 1", | |||||
"查电脑表": "select 字段 from 电脑表 where 年级 = 1", | |||||
"查全校信息": "select 字段 from 信息表" | |||||
} | |||||
``` | |||||
就能自动生成 SQL 了,还可以查看调用关系,非常清晰: | |||||
![](./doc/assets/complex-example-result.png) | |||||
## 实现 | ## 实现 | ||||
@@ -59,6 +59,14 @@ const getSQL = () => { | |||||
console.log(invokeTree.value); | console.log(invokeTree.value); | ||||
}; | }; | ||||
const importExampleAndCal = () => { | |||||
if (inputEditor.value) { | |||||
const exampleJSON = importExample("complex"); | |||||
toRaw(inputEditor.value).setValue(exampleJSON); | |||||
inputEditor.value.getAction("editor.action.formatDocument").run(); | |||||
} | |||||
}; | |||||
const showInvokeTree = () => { | const showInvokeTree = () => { | ||||
if (!invokeTree.value) { | if (!invokeTree.value) { | ||||
getSQL(); | getSQL(); | ||||
@@ -121,7 +129,7 @@ onMounted(() => { | |||||
<a-button size="large" type="primary" ghost @click="showInvokeTree"> | <a-button size="large" type="primary" ghost @click="showInvokeTree"> | ||||
查看调用树 | 查看调用树 | ||||
</a-button> | </a-button> | ||||
<a-button size="large" type="default" @click="importExample('init')"> | |||||
<a-button size="large" type="default" @click="importExampleAndCal"> | |||||
导入例子 | 导入例子 | ||||
</a-button> | </a-button> | ||||
</a-space> | </a-space> | ||||
@@ -0,0 +1,31 @@ | |||||
{ | |||||
"main": "select (a / b - 1) from (@查整体(date = 今天)) a, (@查整体(date = 昨天)) b", | |||||
"查整体": "@查年级() union @查1班() union @查2班() where date = #{date}", | |||||
"查年级": "@查汇总_性别汇总() union @查汇总_性别分组() union @查汇总_爱好汇总() union @查汇总_爱好分组() union @查汇总_电脑类别汇总() union @查汇总_电脑类别分组()", | |||||
"查汇总_性别汇总": "@查除电脑关联表()", | |||||
"查汇总_性别分组": "@查除电脑关联表() group by 性别", | |||||
"查汇总_爱好汇总": "@查除电脑关联表()", | |||||
"查汇总_爱好分组": "@查除电脑关联表() where 爱好 in (xx) group by 爱好", | |||||
"查汇总_电脑类别汇总": "@查除三连和学习表()", | |||||
"查汇总_电脑类别分组": "@查除三连和学习表() group by 电脑类别", | |||||
"查1班": "@查1班_性别汇总() union @查1班_性别分组() union @查1班_爱好汇总() union @查1班_爱好分组() union @查1班_电脑类别汇总() union @查汇总_电脑类别分组()", | |||||
"查1班_性别汇总": "@查除电脑关联表() where 1班", | |||||
"查1班_性别分组": "@查除电脑关联表() where 1班 group by 性别", | |||||
"查1班_爱好汇总": "@查除电脑关联表() where 1班", | |||||
"查1班_爱好分组": "@查除电脑关联表() where 1班 and 爱好 in (xx) group by 爱好", | |||||
"查1班_电脑类别汇总": "@查除三连和学习表() where 1班", | |||||
"查1班_电脑类别分组": "@查除三连和学习表() where 1班 group by 电脑类别", | |||||
"查2班": "@查2班_性别汇总() union @查2班_性别分组() union @查2班_电脑类别汇总() union @查2班_电脑类别分组()", | |||||
"查2班_性别汇总": "@查除电脑关联表() where 2班", | |||||
"查2班_性别分组": "@查除电脑关联表() where 2班 group by 性别", | |||||
"查2班_电脑类别汇总": "@查除三连和学习表() where 2班", | |||||
"查2班_电脑类别分组": "@查除三连和学习表() where 2班 group by 电脑类别", | |||||
"查所有关联表": "@查信息表() left join (@查三连表()) left join (@查学习表()) left join (@查电脑表()) left join (@查全校信息())", | |||||
"查除电脑关联表": "@查信息表() left join (@查三连表()) left join (@查学习表()) left join (@查全校信息())", | |||||
"查除三连和学习表": "@查信息表() left join (@查电脑表()) left join (@查全校信息())", | |||||
"查信息表": "select 字段 from 信息表 where 年级 = 1", | |||||
"查三连表": "select 字段 from 三连表 where 年级 = 1", | |||||
"查学习表": "select 字段 from 学习表 where 年级 = 1", | |||||
"查电脑表": "select 字段 from 电脑表 where 年级 = 1", | |||||
"查全校信息": "select 字段 from 信息表" | |||||
} |
@@ -1,7 +1,9 @@ | |||||
import init from "./init.json"; | import init from "./init.json"; | ||||
import complex from "./complex.json"; | |||||
const exampleMap: Record<string, any> = { | const exampleMap: Record<string, any> = { | ||||
init, | init, | ||||
complex, | |||||
}; | }; | ||||
export const importExample = (key: string) => { | export const importExample = (key: string) => { | ||||
@@ -92,6 +92,9 @@ function replaceParams( | |||||
} | } | ||||
// 动态、静态参数结合,且优先用静态参数 | // 动态、静态参数结合,且优先用静态参数 | ||||
params = { ...(params ?? {}), ...currentNode.params }; | params = { ...(params ?? {}), ...currentNode.params }; | ||||
if (invokeTreeNode) { | |||||
invokeTreeNode.params = params; | |||||
} | |||||
// 无需替换 | // 无需替换 | ||||
if (!params || Object.keys(params).length < 1) { | if (!params || Object.keys(params).length < 1) { | ||||
return sql; | return sql; | ||||