{"id":20837591,"url":"https://github.com/taojy123/sqlx","last_synced_at":"2025-05-08T20:29:56.050Z","repository":{"id":57470660,"uuid":"216957440","full_name":"taojy123/sqlx","owner":"taojy123","description":"强大的 SQL 语法拓展，目标是打造 \"易读易写 方便维护\" 的 sql 脚本 | SQL Extension","archived":false,"fork":false,"pushed_at":"2020-04-10T15:16:21.000Z","size":190,"stargazers_count":29,"open_issues_count":0,"forks_count":7,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-17T22:44:16.949Z","etag":null,"topics":["database","sql","sql-extension","sqlx"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/taojy123.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}},"created_at":"2019-10-23T03:10:26.000Z","updated_at":"2024-09-27T22:44:15.000Z","dependencies_parsed_at":"2022-09-26T17:40:34.753Z","dependency_job_id":null,"html_url":"https://github.com/taojy123/sqlx","commit_stats":null,"previous_names":[],"tags_count":3,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taojy123%2Fsqlx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taojy123%2Fsqlx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taojy123%2Fsqlx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/taojy123%2Fsqlx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/taojy123","download_url":"https://codeload.github.com/taojy123/sqlx/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253144362,"owners_count":21861050,"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":["database","sql","sql-extension","sqlx"],"created_at":"2024-11-18T01:08:03.488Z","updated_at":"2025-05-08T20:29:56.031Z","avatar_url":"https://github.com/taojy123.png","language":"Python","readme":"# sqlx\n\nSQL Extension\n\n强大的 SQL 语法拓展，目标是打造 \"易读易写 方便维护\" 的 sql 脚本\n\n\n## 应用场景\n\n假设有一张商品价目表(product)，每天价格变动的商品都会更新报价。\n\n例如，苹果的最新价格为 10 元, 因为苹果最新的一次报价是在 20191211, 当时价格为 10 元。\n\nname(商品名称)|price(价格)|date(报价日期)\n-|-|-\n苹果|15|20191208\n香蕉|18|20191208\n橘子|12|20191208\n香蕉|16|20191209\n橘子|11|20191209\n苹果|11|20191210\n橘子|13|20191210\n苹果|10|20191211\n香蕉|22|20191211\n橘子|14|20191212\n\n现在要求通过 sql 统计出 `20191212 这天的平均价格 比 20191209 那天涨了多少` ?\n\n正常情况下我们可能会写出这样的 sql\n```sql\nSELECT\n    a1.avg_price AS `20191209 平均价格`,\n    a2.avg_price AS `20191212 平均价格`,\n    (a2.avg_price - a1.avg_price) AS `涨价金额`\nFROM\n    (\n        -- 求出各类别 20191209 前最后一次报价的平均价格\n        SELECT\n            avg(product.price) AS avg_price\n        FROM\n            (\n                -- 求出各商品在 20191209 前最后一次报价的日期\n                SELECT\n                    name,\n                    max(date) AS max_date\n                FROM\n                    product\n                WHERE\n                    date \u003c= '20191209'\n                GROUP BY\n                    name\n            ) AS t1\n        LEFT JOIN product\n        ON t1.name = product.name AND t1.max_date = product.date\n    ) AS a1\nLEFT JOIN\n    (\n        -- 再求出各类别 20191212 前最后一次报价的平均价格\n        SELECT\n            avg(product.price) AS avg_price\n        FROM\n            (\n                -- 先求出各商品在 20191212 前最后一次报价的日期\n                SELECT\n                    name,\n                    max(date) AS max_date\n                FROM\n                    product\n                WHERE\n                    date \u003c= '20191212'\n                GROUP BY\n                    name\n            ) AS t2\n        LEFT JOIN product\n        ON t2.name = product.name AND t2.max_date = product.date\n    ) AS a2\nON true\n```\n\n得到统计结果如下:\n\n20191209 平均价格|20191212 平均价格|涨价金额\n-|-|-\n14.0000|15.3333|1.3333\n\n\n传统做法虽然得到的结果是正确的，但同时暴露出以下问题:\n1. 子查询多层嵌套，代码可读性极低\n2. `t1` `t2` 两个子查询内容基本一致，也就说我们要维护两处相同的代码\n3. `a1` `a2` 两个子查询也基本一致，并且其中相同的注释我们要写两遍，感觉太\"蠢\"了\n4. 这只是个很简单的示例，在实际工作中，针对更复杂的统计需求，代码的复杂度将会以指数形式递增\n\n\n下面看看如何使用 sqlx 来解决上述问题:\n```sql\nfunc product_max_date(day)\n    -- 子查询: 统计出各个商品在 {day} 前最后一次报价的日期\n    (\n        SELECT\n            name,\n            max(date) AS max_date\n        FROM\n            product\n        WHERE\n            date \u003c= '{day}'\n        GROUP BY\n            name\n    )\nend\n\nfunc date_avg_price(day):\n    -- 子查询: 统计出 {day} 这天各个类别的平均价格\n    (\n        SELECT\n            avg(product.price) AS avg_price\n        FROM\n            {product_max_date($day)} AS t1\n        LEFT JOIN product \n        ON t1.name = product.name AND t1.max_date = product.date\n    )\nend\n\nSELECT\n    a1.avg_price AS `20191209 平均价格`,\n    a2.avg_price AS `20191212 平均价格`,\n    (a2.avg_price - a1.avg_price) AS `涨价金额`\nFROM\n    {date_avg_price(20191209)} AS a1\nLEFT JOIN \n    {date_avg_price(20191212)} AS a2\nON true\n```\n\n\n优势非常明显:\n1. 核心代码是一段短小的 `SELECT`，外加两个子查询的定义就搞定了，代码逻辑清晰，可读性高\n2. `a1` `a2` 使用类似 `函数` 的概念进行封装，通过传入不同的参数来生成不同的子查询内容\n3. 相同逻辑的代码片段只需要写一遍，大大降低了代码维护的工作量\n4. 使用 sqlx 提供的编译工具或插件，可快速编译成 sql 代码，在数据库中执行结果一致\n\n\n## 语法简介\n\n### 1. 通过 `var` 定义变量，可在脚本中反复引用\n\n示例:\n```sql\nvar field_name = age\nvar field_value = 30\n\nSELECT {field_name} from students WHERE {field_name} \u003c {field_value};\nSELECT {field_name} from teachers WHERE {field_name} \u003e {field_value};\n```\n\n编译生成 sql 为:\n```sql\nSELECT age from students WHERE age \u003c 30;\nSELECT age from teachers WHERE age \u003e 30;\n```\n\n\n### 2. 通过 `func` 定义脚本片段，并反复引用\n\n示例:\n```sql\n-- ! 定义片段\nfunc good_students(score):\n    (\n        SELECT\n            *\n        FROM\n            students\n        WHERE\n            score \u003e {score}\n    ) AS good_students\nend\n\nSELECT name FROM {good_students(80)};\nSELECT count(*) FROM {good_students(80)};\n```\n\n编译生成 sql 为:\n```sql\nSELECT name FROM \n    (\n        SELECT\n            *\n        FROM\n            students\n        WHERE\n            score \u003e 80\n    ) AS good_students\n;\nSELECT count(*) FROM \n    (\n        SELECT\n            *\n        FROM\n            students\n        WHERE\n            score \u003e 80\n    ) AS good_students\n;\n```\n\n\n### 3. 循环\n通过 `for` 批量循环生成脚本（暂不支持循环嵌套）\n\n\n\n示例1:\n```sql\n{% for n in table1,table2,table3 %}\n    SELECT * FROM {n};\n{% endfor %}\n```\n\n编译生成 sql 为:\n```sql\nSELECT * FROM table1;\nSELECT * FROM table2;\nSELECT * FROM table3;\n```\n\n\n示例2:\n```sql\n{% for n|m in table1|id,table2|name,table3|age %}\n    SELECT {m} FROM {n};\n{% endfor %}\n```\n\n编译生成 sql 为:\n```sql\nSELECT id FROM table1;\nSELECT name FROM table2;\nSELECT age FROM table3;\n```\n\n\n### 4. 判断\n通过 `if` 生成逻辑分支脚本（暂不支持 if 嵌套）\n\n\n示例1:\n```sql\nvar a 8\n\n{% if $a \u003e 4 %}\n    SELECT * FROM table1;\n{% endif %}\n```\n\n编译生成 sql 为:\n```sql\nSELECT * FROM table1;\n```\n\n示例2:\n```sql\n{% for n in table1,table2,table3 %}\n    {% if $n == table1 %}\n        SELECT id, name FROM {n};\n    {% else %}\n        SELECT * FROM {n};\n    {% endif %}\n{% endfor %}\n```\n\n编译生成 sql 为:\n```sql\nSELECT id, name FROM table1;\nSELECT * FROM table2;\nSELECT * FROM table3;\n```\n\n\n更多示例可参考 [demo.sqlx](https://github.com/taojy123/sqlx/blob/master/demo.sqlx)\n\n\n### 5. 处理 `{` `}` 字符\n\n如果你需要在生成的 sql 内容中包含 `{` `}` 这样的字符，不能直接在 sqlx 中写 `{` 或 `}`，因为这样会被认为是变量引用的起止标记\n\n你需要在这些字符前加上一个转义符（默认是`\\`），如 `\\{` `\\}` 这样即可\n\n示例:\n```sql\nvar cc dd\nSELECT * FROM table1 WHERE name = 'aa\\{bb\\}{cc}'\n```\n\n编译生成 sql 为:\n```sql\nSELECT * FROM table1 WHERE name = 'aa{bb}dd'\n```\n\n\n### 6. 使用 `import` 导入模块\n\n通过 import 可以引入现有的 sqlx 脚本文件作，但只能导入其中的 var 和 func\n\n如果在当前脚本有重复同名变量或 func，会被覆盖以当前脚本为准\n\n示例:\n```sql\n-- mod.sqlx\nvar colume  name\nvar colume2 score\n\nfunc good_students(score):\n    (\n        SELECT\n            *\n        FROM\n            students\n        WHERE\n            score \u003e {score}\n    ) AS good_students\nend\n```\n\n```sql\nimport mod\nvar colume2 age\nSELECT {colume} from teachers WHERE {colume2} \u003e 10;\nSELECT name FROM {good_students(60)};\nSELECT count(*) FROM {good_students(80)};\n```\n\n编译生成 sql 为:\n```sql\nSELECT name from teachers WHERE age \u003e 10;\nSELECT name FROM \n    (\n        SELECT\n            *\n        FROM\n            students\n        WHERE\n            score \u003e 60\n    ) AS good_students\n;\nSELECT count(*) FROM \n    (\n        SELECT\n            *\n        FROM\n            students\n        WHERE\n            score \u003e 80\n    ) AS good_students\n;\n```\n\n\n-------\n\n## 安装使用\n\nWindows 64位系统，可以直接下载 [sqlx.exe](https://github.com/taojy123/sqlx/releases) \n\n双击运行，即可将当前目录下的 `sqlx 脚本文件 `编译为 `sql`， 放置于 `dist` 目录中。\n\n\n-------\n\n## 使用 Sublime Text 插件\n\n\nSqlx 插件已被 `Sublime Text` 官方收录。\n\n可搜索安装 `Sqlx Builder` 插件，在 Build System 中选择 `Sqlx`，可快捷将 sqlx 脚本编译为 sql。\n\n![](https://raw.githubusercontent.com/taojy123/SublimeText-Sqlx/master/sqlx.png)\n\n![](https://raw.githubusercontent.com/taojy123/SublimeText-Sqlx/master/sqlx2.png)\n\n-------\n\n\n## 其他系统平台，可通过 Python3 安装使用\n\n如果你的系统无法运行 `sqlx.exe`，可以先安装 [Python3](https://www.python.org/downloads/)，然后使用 `pip` 命令一键安装\n\n```\npip install sqlx\n```\n\n\n### 使用 `sqlx` 命令行工具\n\n1. 安装后直接执行 `sqlx` 命令，可一键编译当前目录下的所有 `.sqlx 脚本文件`\n```\n$ ls\ntest1.sqlx    test2.sqlx\n\n$ sqlx\ndist/test1.sql built\ndist/test2.sql built\nFinish!\n\n$ ls dist\ntest1.sql    test2.sql\n```\n\n\n2. `sqlx` 命令后跟随目录路径参数，可编译指定路径下的所有脚本\n```\n$ ls test\ntest3.sqlx    test4.sqlx\n\n$ sqlx ./test/\ntest/dist/test3.sql built\ntest/dist/test4.sql built\nFinish!\n\n$ ls test/dist\ntest3.sql    test4.sql\n```\n\n\n3. `sqlx` 命令后跟随文件路径参数，可编译指定的单个脚本\n```\n$ sqlx ./test/test3.sqlx\ntest/dist/test3.sql built\nFinish!\n\n$ ls test/dist\ntest3.sql\n```\n\n\n### 在 Python3 程序中使用 `sqlx.build` 方法\n```python\nimport sqlx\n\nmy_script = \"\"\"\n{% for n in table1,table2,table3 %}\n    {% if $n == table1 %}\n        SELECT id, name FROM {n};\n    {% else %}\n        SELECT * FROM {n};\n    {% endif %}\n{% endfor %}\n\"\"\"\n\nsql = sqlx.build(my_script, pretty=True)\nprint(sql)\n```\n\n\n-------\n\n\n## 版本更新说明 \n\n\n### v0.2.0\n\n为提高脚本书写体验，变更了语法关键词\n\n- `define` 改成 `var`\n- `block .. endblock` 改成 `func .. end`\n\n老版本语法目前依旧兼容\n\n\n### v0.1.1\n\n第一个可用版本发布\n\n- 支持 `escape` （默认`\\`）\n- 自动复制编译后的 `sql` 进剪切板\n- 支持 import 导入 sqlx 脚本模块\n\n\n### v0.1.0\n\n第一个可用版本发布\n\n- 支持 `var` 语法\n- 支持 `func` 语法\n- 支持 `for` 语法\n- 支持 `if`  语法\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaojy123%2Fsqlx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftaojy123%2Fsqlx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftaojy123%2Fsqlx/lists"}