- content
概述
sybn sql 是基于 SQL-92 扩展的多语句流水线查询语言。它在标准 SQL 基础上增加了三类能力:
- 流水线——多条语句用分号
;连接,前一条的结果自动传递给后一条。 - 非标命令——20+ 个扩展命令,覆盖列操作、行列转换、批量执行、缓存、调试等场景。
- 异构数据源——可在一条 SQL 中联合查询 mysql、mongo、es、redis、json、csv 等。
本文是 sybn sql 的执行模型总览,适合作为 nl2sql(自然语言转 SQL)应用的命令选择参考。
一、流水线执行模型
1.1 分号多语句
sybn sql 用分号 ; 连接多条语句,按顺序执行:
select * from users where age > 18;
appendColumns(now() as query_time);
removeColumns(password);
执行流程:
- 第一条
select查询用户表,结果压入堆栈。 - 第二条
appendColumns从堆栈取出结果,追加query_time列,新结果压栈。 - 第三条
removeColumns从堆栈取出结果,删除password列,新结果压栈。 - 最终返回栈顶结果。
1.1.1 流水线承接规则(重要)
流水线只有最后一个语句的结果会返回,且多语句之间必须有承接关系——后一条语句必须能消费前一条压栈的结果。每条命令对堆栈有明确的「消费量 / 生产量」(见 1.2 节分类):
| 写法 | 是否合法 | 说明 |
|---|---|---|
select ...; appendColumns(...) |
✅ | convert 消费 1 个 source 结果 |
select ...; removeColumns(...); pivot(...) |
✅ | 多个 convert 串联 |
select ...; execBatch(子sql) on ... |
✅ | execBatch 消费 1 个 source 作为输入列表 |
select A; select B; left join ... |
✅ | join 消费 2 个 source,是「两个 select」唯一合法的用法 |
select 1; select 2 |
❌ | 两个独立 select 串联,无命令消费,会报错 |
select ...; select ... from @sybn_last_select@table |
⚠️ | 变量承接在此环境可能失败 |
select ...; clearQueue(); select ... |
✅ | clearQueue 清栈后可重新 select |
多步骤查询的正确写法:用一条 select + 子查询 + 转换命令组合。如果需要合并两个独立查询结果,用 join(而不是串联两个 select)。如果确实需要分步,用 execBatch 或 cacheResult 承接。
1.2 堆栈机制
每条语句执行后,结果会被压入一个 stackData 堆栈:
语句1: select ... → 压栈 [结果1]
语句2: appendColumns → 弹出[结果1], 处理后压栈 [结果2]
语句3: removeColumns → 弹出[结果2], 处理后压栈 [结果3]
↑ 最终返回
- 默认返回栈顶(最后一条语句的结果)。
return命令可以指定返回堆栈中的任意一层。clearQueue命令可以清空堆栈。
1.3 命令的流水线行为分类(核心)
每条命令对 stackData 堆栈有明确的「消费量 / 生产量」,这是判断能否承接的依据(对应源码 SySqlCommandConverExecutor.getConsumed()):
| 分类 | 消费栈顶 | 压栈 | 命令 | 注入变量 |
|---|---|---|---|---|
| source(产生数据) | 0 | 1 | select、union all/union、intersect/except、with as、execBatch(子sql)、dynamicSourceQuery、cacheResult |
@sybn_last_select@table |
| convert(转换栈顶) | 1 | 1 | appendColumns、prependColumns、removeColumns、alignColumns、splitColumns、builderInsert、pivot、unwind、unnest、fill |
@sybn_last_conver@table |
| multi-input(多输入转换) | 2 | 1 | INNER/LEFT/RIGHT/FULL JOIN、lookup |
@sybn_last_join@table |
| stack-control(清栈) | N 或全部 | 0 | clearQueue([N]) |
— |
| control-flow(控制流) | 0 | 0 | return、if exists、explain |
— |
| auxiliary(辅助) | 0 | 0 | set @var、show tables/databases/functions、desc、show value stat |
— |
| peek(旁路输出) | 0 | 0 | peekResult |
— |
消费量来自源码:
ConvertSySqlJoin.getConsumed()=2、SySqlExecBatchQueryExecutor.getConsumed()=1、SySqlClearQueueQueryExecutor.getConsumed()=Integer.MAX_VALUE,convert 类命令通过SySqlCommandConverExecutor接口默认消费 1 个。
1.4 子查询嵌套:() 包裹多语句流水线
核心模式:用 () 包裹一个多语句流水线,外层可以接标准 SQL 操作(where / group by / order by / having / limit 等),把流水线结果当作一张表来用。
/* 把用户表和订单 join 后, 外层再按城市分组聚合 */
select city, sum(total) as city_total
from (
/* 第1步: 左表 */
select id, name, city from users where status = 1;
/* 第2步: 右表 */
select user_id, sum(amount) as total from orders group by user_id;
/* 第3步: join 消费栈顶2个, 输出1个 */
left join select(name, city, total) on left.id = right.user_id
) where total > 100 /* 外层过滤 */
group by city /* 外层分组 */
order by city_total desc /* 外层排序 */
limit 10;
关键规则:
()内部是独立流水线,有自己的堆栈,内部多语句必须有承接关系(source → convert/multi-input)。()内部最终返回栈顶,作为外层from的数据源。- 外层可以接任意标准 SQL 子句:
where/group by/having/order by/limit/join等。 ()内部两个独立 select 串联会报错(select 1; select 2无承接),必须用 join 或转换命令承接。
适用场景:多步骤处理(join/转换)后还需要用标准 SQL 做聚合、过滤、排序时,用 () 包裹流水线,外层写标准 SQL。这比把所有逻辑塞进流水线更清晰。
嵌套层级:() 可以多层嵌套,每层都是独立流水线:
select * from (
select * from (
select ...;
select ...;
join ...
) where ... /* 内层流水线 + 外层过滤 */
group by ...
) where ... /* 外层再过滤 */
order by ...
二、命令分类体系
sybn sql 的命令分为 7 大类,nl2sql 时根据意图选择。分类按功能划分;按流水线行为(source/convert/multi-input)划分见 1.3 节。
2.1 查询类(DQL,source)
标准 SQL 查询,产生新数据压栈。from 子句支持多种数据源(库表 + 字面量)。
标准库表查询:
| 命令 | 语法 | 用途 |
|---|---|---|
select |
select ... from 库.表 where ... |
基础查询 |
union / union all |
select ... union all select ... |
联合(去重/不去重) |
intersect / except |
select ... intersect select ... |
交集/差集 |
with as |
with t as (select ...) select * from t |
CTE 临时表 |
内联字面量数据源(无需查库,数据写在 SQL 里,源码 TableLoader.loadList()):
| 数据源 | 语法 | 示例 |
|---|---|---|
| JSON 数组 | from [{...},{...}] |
select * from [{a:1,b:1},{a:2,b:2}] |
| 引号 JSON | from "[{...}]" |
select * from "[{a:1},{a:2}]" |
| CSV 文本块 | from csv(三反引号包裹的CSV文本) |
select * from csv(三反引号 name,age 换行 张三,20 三反引号)` |
| TSV 文本块 | from tsv(三反引号包裹的TSV文本) |
select * from tsv(三反引号 name\tage 换行 张三\t20 三反引号)` |
| VALUES | from (VALUES (...),(...)) AS t(列) |
from (VALUES ("v1"),("v2")) AS t(code) |
| factory | from factory({engine:'...',...}) |
factory({engine:'timeScope', key:'date', timeScope:'2026-01-01~2026-01-07'}) |
| generate_series | from generate_series(1, 10) as a |
生成 1-10 整数序列 |
CSV/TSV 用三反引号文本块包裹,第一行表头(列名),其后数据行。详见 from factory 和 web-sql part2。
跨轮次对话数据合并:把上一轮查询结果手动复制为 JSON/CSV 字面量嵌入当前 SQL,用 join 或 union 合并(详见 from-factory 文档)。
/* 本轮数据 union 上一轮结果(JSON字面量) */
select '本轮' as round, cinema_name, amount from orders where date = curdate()
union all
select '上轮' as round, cinema_name, amount
from [{cinema_name:"影城A", amount:1200}, {cinema_name:"影城B", amount:980}]
2.2 转换类(convert,后置命令)
作用于前一条语句的结果,消费栈顶 1 个,转换后压栈 1 个。不改变行数(除 unwind/unnest)。
| 命令 | 用途 | 改变行数 |
|---|---|---|
appendColumns |
结尾追加列 | 否 |
prependColumns |
开头插入列 | 否 |
removeColumns |
删除列(可按空值条件) | 否 |
alignColumns |
对齐/筛选列 | 否 |
splitColumns |
拆分列为多列 | 否 |
builderInsert |
结果转 INSERT 语句 | 否 |
pivot |
行转列 | 否 |
unwind |
数组/Map 拆成多行(可多字段) | 是 |
unnest |
数组/Map 拆成多行(自定义列名) | 是 |
fill |
按维度补齐缺失数据行 | 是 |
2.3 join 类(multi-input,后置命令)
消费栈顶 2 个(左表 + 右表),join 后输出 1 个结果压栈。前面必须有 2 条 source 语句。源码:ConvertSySqlJoin.getConsumed()=2。
| 命令 | 语法 | 用途 |
|---|---|---|
INNER JOIN |
INNER JOIN SELECT(字段) ON left.x = right.y |
内连接 |
LEFT JOIN |
LEFT JOIN SELECT(字段) ON left.x = right.y |
左连接 |
RIGHT JOIN |
RIGHT JOIN SELECT(字段) ON left.x = right.y |
右连接 |
FULL JOIN |
FULL JOIN SELECT(字段) ON left.x = right.y |
全连接 |
lookup |
lookup SELECT(字段) ON left.x = right.y |
mongo 风格 join |
示例:
/* 将用户表和订单表 join 起来 */
select * from (
select id, name from users;
select user_id, sum(amount) as total from orders group by user_id;
left join select(name, total) on left.id = right.user_id
) order by total desc
语法说明:
SELECT(...):指定返回字段,支持as别名和 udf 函数;左右表同名字段优先返回左表值ON:支持left.x = right.y、x = y、using(字段);多条件用AND连接- 报错信息:
join 需要2张前表,而不是N张.可能是前面的某行代码少写了分号. - 详见 DatasSqlJoinUtil
2.4 后置过滤类
作用于子查询结果,语法比嵌套 select 更简洁。
| 命令 | 语法 | 用途 |
|---|---|---|
where |
from (...) where 条件 |
过滤 |
order by |
from (...) order by 字段 desc |
排序 |
limit |
from (...) limit N |
分页 |
三者可组合:
from (...) where 条件 order by 字段 limit N
2.5 子查询执行类(source / 旁路)
执行一个子 SQL,把结果压栈。常用于批量、动态数据源、缓存。
| 命令 | 用途 | 流水线行为 |
|---|---|---|
execBatch |
批量执行子任务,支持并发/忽略错误 | 消费栈顶1个作为 @item 列表,压栈1个 |
dynamicSourceQuery |
动态切换数据源执行子查询 | source,压栈1个 |
cacheResult |
缓存子查询结果,跨语句复用 | source,压栈1个 |
peekResult |
提前输出中间结果到前端/日志 | peek,旁路不压栈 |
2.6 控制流类(control-flow / stack-control)
控制流水线的执行流程。不产生数据。
| 命令 | 语法 | 用途 | 流水线行为 |
|---|---|---|---|
return |
return [N / select ...] |
提前返回并终止后续 | 不消费栈不压栈 |
if exists |
if exists (select ...) then ... |
条件执行 | 不消费栈不压栈 |
clearQueue |
clearQueue([N]) |
清理堆栈 | 消费 N 个或全部 |
explain |
explain select ... |
查看查询计划(仅 JDBC) | 不消费栈不压栈 |
2.7 辅助类(auxiliary)
不产生数据,用于变量、表结构查看。
| 命令 | 用途 |
|---|---|
set @var := 值 |
设置变量 |
use 数据库 |
切换数据库 |
show tables / show databases |
查看表/库 |
show create table / desc 表 |
查看表结构 |
show functions |
查看所有函数 |
show value stat |
查看字段统计特征 |
三、@变量传递机制
sybn sql 支持 5 种变量,是流水线语句间数据传递的核心:
3.1 特殊上下文变量(自动注入)
这些变量由系统自动注入,引用堆栈中的历史结果:
| 变量 | 含义 | 注入时机 |
|---|---|---|
@sybn_last_select@table |
最后一个 select 的结果 | 每次 select 执行后 |
@sybn_last_conver@table |
最后一个转换命令的结果 | appendColumns/removeColumns 等执行后 |
@sybn_last_join@table |
最后一个 join 的结果 | join 执行后 |
select * from users;
select count(*) as cnt from @sybn_last_select@table;
/* @sybn_last_select@table 被替换为上一条 select 的结果 */
3.2 用户变量(set 赋值)
set @t := str_to_date('2026-01-01', '%Y-%m-%d');
select * from orders where create_time > @t;
- 兼容
:=和=赋值。 - 一般只能替换 where 右侧的值。
3.3 列表变量
set @types@list := (select type from config where enabled = 1);
select * from orders where type in (@types@list);
@var@list用于in查询传值。- 把子查询结果展开为列表。
3.4 外部参数(mybatis 风格)
select * from users where name = ${name} and age > ${age}
${var}由 sql 外部传入,可替换任意位置。#{var}同样支持(预编译占位)。
3.5 全局变量
select * from ${${env}_table}
$${var}引用全局变量,可替换任意位置。
四、执行顺序图解
┌─────────────────────────────────────────────────────────┐
│ 输入: 多语句 SQL(分号分隔) │
└──────────────────────┬──────────────────────────────────┘
↓
┌──────────────────────┐
│ 语句1: select ... │ → 压栈 → 注入 @sybn_last_select@table
└──────────┬───────────┘ (source: 消费0 压栈1)
↓
┌──────────────────────┐
│ 语句2: 转换命令 │ → 弹栈1个处理 → 压栈 → 注入 @sybn_last_conver@table
│ (appendColumns等) │ (convert: 消费1 压栈1)
└──────────┬───────────┘
↓
┌──────────────────────┐
│ 语句3: join │ → 弹栈2个(左+右) → join → 压栈 → 注入 @sybn_last_join@table
│ (left join 等) │ (multi-input: 消费2 压栈1)
└──────────┬───────────┘
↓
┌──────────────────────┐
│ 语句4: 子查询命令 │ → 执行子SQL → 压栈
│ (execBatch等) │ (source: 消费0/1 压栈1)
└──────────┬───────────┘
↓
┌──────────────────────┐
│ 语句N: return │ → 指定返回栈中某层
└──────────┬───────────┘ (control-flow: 不消费不压栈)
↓
返回最终结果
五、nl2sql 命令选择指南
自然语言转 SQL 时,按用户意图映射命令:
| 用户意图 | 推荐命令 | 示例自然语言 |
|---|---|---|
| 查询数据 | select |
“查出所有成年用户” |
| 合并两个查询结果 | INNER/LEFT/RIGHT/FULL JOIN |
“把用户表和订单表按用户id关联” |
| mongo 数据关联 | lookup |
“把 mongo 用户表和订单表关联” |
| 追加计算列 | appendColumns |
“加一列显示年龄是否大于60” |
| 删除敏感列 | removeColumns |
“去掉密码和手机号列” |
| 行转列 | pivot |
“把按月份的行数据转成列” |
| 列转行 | unwind / unnest |
“把 tags 数组拆成多行” |
| 补齐缺失日期 | fill |
“把缺少的日期补上空行” |
| 批量执行 | execBatch |
“对每个用户分别查询订单” |
| 跨库查询 | dynamicSourceQuery |
“从另一个 mysql 库查数据” |
| 缓存慢查询 | cacheResult |
“这个结果缓存一下复用” |
| 条件执行 | if exists |
“如果表存在就查询” |
| 提前返回 | return |
“拿到结果就停止” |
| 数据脱敏 | masking() 函数 |
“手机号中间4位打星号” |
六、相关文档
命令详解
- 列操作命令(align/append/prepend/remove/split)
- builderInsert
- clearQueue
- peekResult
- return
- explain
- unnest / unwind
- 后置 where/order/limit
- execBatch
- dynamicSourceQuery
- cacheResult
- pivot
- if exists
- fill
- join / lookup(多输入转换)
内联数据源
- from factory(factory/generate_series)
- web-sql part2(from JSON 数组)
- 源码:
sybn-core/src/main/java/cn/sybn/util/io/db/common/helper/loader/TableLoader.java— from 字面量数据源加载(JSON/CSV/TSV/VALUES/factory/generate_series)