{"id":13459611,"url":"https://github.com/liyupi/sql-generator","last_synced_at":"2025-05-14T15:10:15.862Z","repository":{"id":37234272,"uuid":"491420114","full_name":"liyupi/sql-generator","owner":"liyupi","description":"🔨 用 JSON 来生成结构化的 SQL 语句，基于 Vue3 + TypeScript + Vite + Ant Design + MonacoEditor 实现，项目简单（重逻辑轻页面）、适合练手~","archived":false,"fork":false,"pushed_at":"2024-01-17T04:01:34.000Z","size":725,"stargazers_count":3445,"open_issues_count":14,"forks_count":703,"subscribers_count":20,"default_branch":"master","last_synced_at":"2025-04-12T10:19:42.047Z","etag":null,"topics":["ant-design","bigdata","hive","javascript","json","monaco-editor","mysql","spark","sql","typescript","vite","vue","vue3"],"latest_commit_sha":null,"homepage":"http://sql.yupi.icu","language":"Vue","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/liyupi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-05-12T08:05:38.000Z","updated_at":"2025-04-11T00:36:32.000Z","dependencies_parsed_at":"2024-01-11T20:42:15.954Z","dependency_job_id":"83c327d5-33db-44c0-8c06-4023b6a0ae2b","html_url":"https://github.com/liyupi/sql-generator","commit_stats":{"total_commits":17,"total_committers":3,"mean_commits":5.666666666666667,"dds":"0.11764705882352944","last_synced_commit":"cd1c1fb8cbfdd26943924abbbebb6bd0fcdf4e5b"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liyupi%2Fsql-generator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liyupi%2Fsql-generator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liyupi%2Fsql-generator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liyupi%2Fsql-generator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/liyupi","download_url":"https://codeload.github.com/liyupi/sql-generator/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254170050,"owners_count":22026219,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ant-design","bigdata","hive","javascript","json","monaco-editor","mysql","spark","sql","typescript","vite","vue","vue3"],"created_at":"2024-07-31T10:00:22.596Z","updated_at":"2025-05-14T15:10:15.811Z","avatar_url":"https://github.com/liyupi.png","language":"Vue","readme":"# 结构化 SQL 生成器\n\n\u003e 用 JSON 来轻松生成复杂的 SQL，大幅提高写 SQL 的效率！\n\u003e \n\u003e by 程序员鱼皮\n\n在线使用：http://sql.yupi.icu\n\n项目介绍视频：https://www.bilibili.com/video/BV1qa411J7vh/\n\n![工具截图](./doc/assets/main.png)\n\n## 项目作用\n\n1. 将 SQL 的编写逻辑 `结构化` ，像写文章大纲一样编写和阅读 SQL\n2. 重复的 SQL 只需编写一次 ，SQL 变动时修改一处即可\n3. 可以针对某部分 SQL 进行传参和调试\n4. 查看 SQL 语句的引用树和替换过程，便于分析理解 SQL\n\n![查看调用树和替换过程](./doc/assets/invokeTree.png)\n\n## 应用场景\n\n如果你要写一句复杂的 SQL，且 SQL 中很多代码是 **相似** 但又不相同的。那么推荐使用该工具，可以不用重复编写 SQL，更有利于修改、维护和理解。\n\n尤其是在大数据分析场景下，经常会有编写复杂 SQL 的需求。 \n\n\u003e 之所以会有这个轮子，也正是因为鱼皮在工作中要写一句长达 3000 行的 SQL 来离线分析数据，手写真的人要疯了！\n\n当然，你也完全可以把它当做一个 `重复代码生成器` ~\n\n## 示例\n\n需求：计算 id = 1 和 id = 2 的两位同学的身高差\n\nSQL 大概是这样的：\n\n```sql\nselect (s1.height - s2.height) as 身高差\nfrom \n(select * from student where id = 1) s1,\n(select * from student where id = 2) s2\n```\n\n显然，上述 SQL 中学生表查询了 2 次，而且除了查询的 id 不同外，查询逻辑完全一致！\n\n如果后面查询学生的逻辑发生修改，那么以上 2 个子查询都要同时修改，不利于维护。\n\n而使用本工具，只需编写如下 JSON，就能自动生成完整的 SQL 了：\n\n```json\n{\n    \"main\": \"select @身高差() from (@学生表(id = 1)) s1, (@学生表(id = 2)) s2\",\n    \"身高差\": \"(s1.height - s2.height) as 身高差\",\n    \"学生表\": \"select * from student where id = #{id}\"\n}\n```\n\n通过类似 `函数调用 + 传参` 的方式，我们无需重复编写 SQL，而且整个 SQL 的逻辑更清晰！\n\n当然，以上只是一个示例，真实大数据离线分析的场景下，SQL 可比这复杂 N 倍！\n\n如果感兴趣的话，欢迎往下看文档，还有更复杂的示例~\n\n## 优势\n\n1. 支持在线编辑 JSON 和 SQL，支持代码高亮、语法校验、一键格式化、查找和替换、代码块折叠等，体验良好\n2. 支持一键生成 SQL\n3. 支持参数透传，比如 @a(xx = #{yy})，yy 变量可传递给 @a 公式\n4. 支持嵌套传参（将子查询作为参数），比如 @a(xx = @b(yy = 1))\n5. 不限制用户在 JSON 中编写的内容，因此该工具也可以作为重复代码生成器来使用\n6. 支持查看 SQL 语句的调用树和替换详情，便于分析引用关系\n\n## 文档\n\n可以把下面的代码放到生成器中试试，一下就明白如何使用啦~\n\n```json\n{\n  \"main\": \"必填, 代码从这里开始生成, 用 @规则名() 引用其他语句\",\n  \"规则名\": \"可以编写任意 SQL 语句 @规则名2() @动态传参(a = 求给 ||| b = star)\",\n  \"规则名2\": {\n    \"sql\": \"用 #{参数名} 指定可被替换的值\",\n    \"params\": {\n      \"参数名\": \"在 params 中指定静态参数, 会优先被替换\"\n    }\n  },\n  \"动态传参\": \"#{a}鱼皮#{b}\"\n}\n```\n\n### 补充说明\n\n`对象键`：定义 SQL 生成规则名称，main 表示入口 SQL，从该 SQL 语句开始生成。\n\n`对象值`：定义具体生成规则。可以是 SQL 字符串或者对象。\n\n`sql`：定义模板 SQL 语句，可以是任意字符串，比如一组字段、一段查询条件、一段计算逻辑、完整 SQL 等。\n\n`params`：静态参数，解析器会优先将该变量替换到当前语句的 #{变量名} 中\n\n`#{xxx}`：定义可被替换的变量，优先用当前层级 params 替换，否则由外层传递\n\n`@xxx(yy = 1 ||| zz = #{变量})`：引用其他 SQL，可传参，参数可再用变量来表示，使用 |||（三个竖线）来分隔参数。\n\n## 复杂示例\n\n需求：用一句 SQL 查询出以下表格\n\n![](./doc/assets/complex-example.png)\n\n这个表格的难点在哪？\n\n1. 查汇总和查明细的粒度不同，不能用 group by 区分，只能用 union（红色）\n2. 分类列中不同行的数据有交叉，不能用 group by 区分，只能用 union\n3. 每一列由多张表共同 join 而成，且不同分类可关联的表不同，须进行区分（灰色表示无法关联），并将缺失的字段补齐（否则无法 union）\n4. 不同行的同一列计算公式可能不同（蓝色）\n5. 不同列的过滤条件不同（比如最后两列墨绿色是要查全校，其余列只查 1 年级）\n6. 要查询同环比，只能用 2 份完整的数据去 join 然后错位计算来得出\n\n显然，这个表中很多查询逻辑是重复但又不同的。\n\n这么算下来，最后这个 SQL 中到底会包含多少个基础表的 select 呢？每个基础表查询要重复编写多少遍呢？\n\n然而，这个表格也只是鱼皮对实际需求简化后才得来的，实际需求比这还复杂几倍！\n\n可想而知，人工写有多恶心？！\n\n但是使用本工具，只需编写如下结构化的 JSON：\n\n```json\n{\n\t\"main\": \"select (a / b - 1) from (@查整体(date = 今天)) a, (@查整体(date = 昨天)) b\",\n\t\"查整体\": \"@查年级() union @查1班() union @查2班() where date = #{date}\",\n\t\"查年级\": \"@查汇总_性别汇总() union @查汇总_性别分组() union @查汇总_爱好汇总() union @查汇总_爱好分组() union @查汇总_电脑类别汇总() union @查汇总_电脑类别分组()\",\n\t\"查汇总_性别汇总\": \"@查除电脑关联表()\",\n\t\"查汇总_性别分组\": \"@查除电脑关联表() group by 性别\",\n\t\"查汇总_爱好汇总\": \"@查除电脑关联表()\",\n\t\"查汇总_爱好分组\": \"@查除电脑关联表() where 爱好 in (xx) group by 爱好\",\n\t\"查汇总_电脑类别汇总\": \"@查除三连和学习表()\",\n\t\"查汇总_电脑类别分组\": \"@查除三连和学习表() group by 电脑类别\",\n\n\t\"查1班\": \"@查1班_性别汇总() union @查1班_性别分组() union @查1班_爱好汇总() union @查1班_爱好分组() union @查1班_电脑类别汇总() union @查汇总_电脑类别分组()\",\n\t\"查1班_性别汇总\": \"@查除电脑关联表() where 1班\",\n\t\"查1班_性别分组\": \"@查除电脑关联表() where 1班 group by 性别\",\n\t\"查1班_爱好汇总\": \"@查除电脑关联表() where 1班\",\n\t\"查1班_爱好分组\": \"@查除电脑关联表() where 1班 and 爱好 in (xx) group by 爱好\",\n\t\"查1班_电脑类别汇总\": \"@查除三连和学习表() where 1班\",\n\t\"查1班_电脑类别分组\": \"@查除三连和学习表() where 1班 group by 电脑类别\",\n\n\t\"查2班\": \"@查2班_性别汇总() union @查2班_性别分组() union @查2班_电脑类别汇总() union @查2班_电脑类别分组()\",\n\t\"查2班_性别汇总\": \"@查除电脑关联表() where 2班\",\n\t\"查2班_性别分组\": \"@查除电脑关联表() where 2班 group by 性别\",\n\t\"查2班_电脑类别汇总\": \"@查除三连和学习表() where 2班\",\n\t\"查2班_电脑类别分组\": \"@查除三连和学习表() where 2班 group by 电脑类别\",\n\n\t\"查所有关联表\": \"@查信息表() left join (@查三连表()) left join (@查学习表()) left join (@查电脑表()) left join (@查全校信息())\",\n\t\"查除电脑关联表\": \"@查信息表() left join (@查三连表()) left join (@查学习表()) left join (@查全校信息())\",\n\t\"查除三连和学习表\": \"@查信息表() left join (@查电脑表()) left join (@查全校信息())\",\n\t\"查信息表\": \"select 字段 from 信息表 where 年级 = 1\",\n\t\"查三连表\": \"select 字段 from 三连表 where 年级 = 1\",\n\t\"查学习表\": \"select 字段 from 学习表 where 年级 = 1\",\n\t\"查电脑表\": \"select 字段 from 电脑表 where 年级 = 1\",\n\t\"查全校信息\": \"select 字段 from 信息表\"\n}\n```\n\n就能自动生成 SQL 了，还可以查看调用关系，非常清晰：\n\n![](./doc/assets/complex-example-result.png)\n\n\n## 实现\n\n使用和 JSON 相性最好的 JavaScript 来实现，编写一份逻辑 JS 文件，可同时应用于 browser 和 server 端。\n\n功能比较轻量，因此选择优先在纯 browser 端实现。\n\n前端使用 `Vue3 + Vite + Ant Design Vue` 开发界面，选用 `Monaco Editor` 实现代码编辑、高亮、格式化等功能，使用 `TypeScript + ESLint` 保证代码规范。\n\nSQL 生成逻辑如下：\n\n1. JSON 字符串转对象\n2. 从入口开始，先替换 params 静态参数，得到当前层解析\n3. 对 @xxx 语法进行递归解析，递归解析时，优先替换静态参数，再替换外层传来的调用参数\n4. 得到最终 SQL\n\n解析器原本采用正则非贪婪替换方式实现，但无法实现嵌套调用，比如 @a(xx = @b())，会被识别为 @a(xx = @b()，匹配到了最近的右括号。 \n因此针对括号嵌套的情况对子查询替换算法做了优化，已支持包含括号语句的嵌套调用。\n\n","funding_links":[],"categories":["Vue","语言资源库"],"sub_categories":["typescript"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliyupi%2Fsql-generator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliyupi%2Fsql-generator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliyupi%2Fsql-generator/lists"}