高性能企业级数据底座
国产自研 SQL 数据引擎
以自研数据库为内核的新一代国产数据基础设施 —— 高阶分析 SQL 全集、跨方言统一、海量数据永不 OOM, 单 jar 嵌入即查即得。
不是「能跑简单查询」的玩具, 而是把完整的分析型能力、跨方言兼容与内存鲁棒性集于一身。
海量数据永不 OOM
排序 / 聚合 / 去重 / JOIN / 窗口 / 集合运算全链路具备弹性内存自适应能力, 本地盘扛住单机内存放不下的数据, 大数据量稳如磐石。
极致单机性能
中等规模数据下亚秒级响应, 读取路径逼近甚至反超手写极致优化代码, 无集群调度开销, 启动即查询。
高阶分析语法齐全
窗口全集 + GROUPS 帧、GROUPING SETS/CUBE/ROLLUP、PIVOT、MATCH_RECOGNIZE、QUALIFY、分位与近似聚合全部到位;尤其 MATCH_RECOGNIZE 不只是能跑 Demo, 而是覆盖 Oracle 级高阶语义。
跨方言统一层
一个引擎同时听懂 MySQL / Oracle / PostgreSQL / SQL Server 的函数与语法, 业界独有;绝大多数存量 SQL 原样直跑, 迁移从「改写工程」降级为「回归验证」。
原生级 JSON 处理
14 个标准 JSON 函数 + ->/->> 箭头语法 + 按需精准提取, 大 JSON 列近乎恒定内存开销。
复杂子查询深度优化
多层嵌套复杂关联子查询智能优化, 内层数据只计算一次, 告别逐行慢扫;并能识别业界普遍退化的边界形态。
单机 ⇄ 分布式 · 灵活扩展
同一套自研内核:嵌入式单 jar 单机极致, 亦可服务化、按数据规模横向扩展为分布式计算节点;开放数据源接入, 自定义灵活扩展, 单机与分布式都是自研、自主可控的实现。
结果可信 · 零静默错误
不支持的边界一律给出明确报错与改写建议, 绝不悄悄返回似是而非的结果 —— 慢可以等, 数错了会误导决策;给管理层看的数, 敢签字。
与传统关系库、大数据 / 流批引擎、列存分析库全面对照。
| 能力维度 | YoungsData | MySQL 8 | PostgreSQL | Oracle | SQL Server | Hive/Spark | Flink | Trino | DuckDB |
|---|---|---|---|---|---|---|---|---|---|
| 基础查询 (SELECT / JOIN / 分组) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 窗口函数 (18 个) | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| 多维聚合 ROLLUP / CUBE / GROUPING SETS | ✓ | ◐ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ | ✓ |
| QUALIFY 窗口结果直接过滤 | 领先 | — | — | — | — | ◐ | — | ✓ | ✓ |
| MATCH_RECOGNIZE 行模式识别 (完整 NFA) | 领先 | — | — | ✓ | — | — | ✓ | ✓ | — |
| PIVOT / UNPIVOT 行列转置 | 领先 | — | — | ✓ | ✓ | ✓ | — | ◐ | ✓ |
| GROUPS 窗口帧 (SQL:2011 高阶帧) | 领先 | — | ✓ | ✓ | — | ◐ | ◐ | ✓ | ✓ |
| WITHIN GROUP 分位 / 中位 | ✓ | — | ✓ | ✓ | ◐ | ◐ | ◐ | ◐ | ✓ |
| 近似聚合 HLL / 海量数据快速估算 | ✓ | — | ◐ | ✓ | ◐ | ✓ | ◐ | ✓ | ✓ |
| 复杂关联子查询智能优化 | ✓ | ◐ | ✓ | ✓ | ✓ | ✓ | ◐ | ✓ | ✓ |
| JSON 高效处理 (恒定内存) | 领先 | ◐ | ◐ | ◐ | ◐ | ◐ | ◐ | ◐ | ✓ |
| 跨方言函数 / 语法兼容 | 独有 | — | — | — | — | ◐ | — | ◐ | ◐ |
| 海量数据永不 OOM (弹性内存) | 领先 | ◐ | ◐ | ◐ | ◐ | ✓ | ✓ | ✓ | ◐ |
| 嵌入式部署 (单 jar · 零集群) | 领先 | — | — | — | — | — | — | — | ✓ |
| 中等数据查询延迟 | 极低 | 低 | 低 | 低 | 低 | 高 | 中 | 中 | 低 |
注:Hive/Spark/Flink 为大数据 / 流批引擎, 与本引擎(嵌入式 OLAP)定位不同, 此处仅就 SQL 分析语法与查询特性维度对照。
完整的分析型 SQL 语法收入囊中, 且几乎所有「不支持」都是带改写提示的明确报错, 而非静默错误。
| 语法类别 | 支持 | 覆盖明细 |
|---|---|---|
| 查询骨架 | ✓ | SELECT / WHERE / GROUP BY / HAVING / ORDER BY / DISTINCT,LIMIT·OFFSET·FETCH 高效分页,WITH RESUME / RESUME ? 断点续读(按令牌定位, 并发写下不重读已交付行, 令牌经 SELECT LAST_RESUME_TOKEN() 取回;独立服务形态),GROUP BY / ORDER BY 序号与别名,NULLS FIRST·LAST,Top-N 极速直取 |
| 条件判断 | ✓ | 比较全运算符 / BETWEEN / IN / LIKE / ILIKE / SIMILAR TO / RLIKE·REGEXP / IS [NOT] DISTINCT FROM(NULL 安全)/ IS NULL |
| JOIN | ✓ | INNER / LEFT / RIGHT / FULL / CROSS / NATURAL, 多表 & 自关联, 派生表任意一侧(含外连接、聚合派生表) |
| 集合运算 | ✓ | UNION / UNION ALL / INTERSECT / EXCEPT(排序归并) |
| 子查询 | ✓ | 标量子查询、IN / NOT IN / EXISTS / ANY / ALL / SOME、行构造 IN、多层复杂关联子查询智能优化 |
| CTE 公共表达式 | ✓ | WITH 单个 / 多个、链式相互引用、多次引用的 CTE 智能复用(只算一次,N 处共享) |
| 窗口函数 | ✓ | 排名 / 偏移 / 分桶 / 取值 / 分布 / 聚合作窗口 / 分位窗口(18 个)+ ROWS·RANGE·GROUPS 三种帧 + 命名窗口 + QUALIFY |
| 聚合函数 | ✓ | SUM/COUNT/AVG/MIN/MAX(DISTINCT)、STDDEV/VAR、统计 CORR/COVAR/REGR、分位 PERCENTILE/MEDIAN、布尔聚合、GROUP_CONCAT/LISTAGG/STRING_AGG、FILTER(WHERE)、近似 HLL / 海量数据快速估算 |
| 多维聚合 | ✓ | ROLLUP / CUBE / GROUPING SETS / GROUPING(_ID) |
| 标量函数 (近 100 个) | ✓ | 字符串(~30)/ 正则 / 数学全集 / 日期时间 / 位运算 / 哈希 MD5·SHA·CRC·BASE64 / Oracle 格式化族 TO_CHAR·TO_DATE·TO_NUMBER / CAST·TRY_CAST / CASE·COALESCE·NVL·DECODE |
| JSON | ✓ | 14 个 JSON 函数(提取 / 判断 / 内省 / 构造)+ ->·->> 箭头语法 + 按需精准提取 |
| FROM 高级形态 | ✓ | PIVOT / UNPIVOT、MATCH_RECOGNIZE(完整 NFA 回溯 + Oracle 高阶语义集)、TABLESAMPLE |
| DML | ✓ | INSERT…VALUES(单 / 多行 / 参数化 batch)、单表 UPDATE / DELETE, 标准条件路径完整;DELETE … LIMIT n 有界删除(命中集按物理扫描序取前 n 条);DELETE … RETURNING * 删除并返回被删行(可与 LIMIT 组合, 与普通 DELETE 同为语句级原子;独立服务形态) |
| DDL | ✓ | CREATE TABLE(列注释 / 主键 / 复合主键 / SHARD 分片 / SORT KEY 排序键 / 内联索引 / 行存声明 ROW FIXED·MIN)、ALTER TABLE(RENAME、ADD·DROP·RENAME COLUMN、ADD·DROP PRIMARY KEY、COMMENT、HISTORY RETAIN)、DROP TABLE、TRUNCATE、CREATE [UNIQUE] INDEX / DROP INDEX |
| 元数据内省 | ✓ | DESCRIBE·DESC <表>、SHOW TABLES [LIKE]、SHOW COLUMNS FROM、SHOW INDEXES·INDEX·KEYS FROM、SHOW CREATE TABLE、SHOW SHARDS FROM(分片与物理文件占用)、SHOW HISTORY FOR(历史天表清单), 结果即结果集, 可直接被程序消费 |
| 账号与访问控制 | ✓ | CREATE·ALTER·DROP USER(SUPERUSER 属性建号时声明)、CREATE·DROP ROLE、GRANT·REVOKE(8 动词 × 全局/库/表三级作用域 + 仅全局的 ADMIN, WITH GRANT OPTION;表/库级拦截默认观察模式只审计不拦, 由服务端开关升为强制)、SHOW GRANTS;来源访问规则 CREATE·DROP ACCESS RULE / SHOW ACCESS RULES(「账号 × 网段」有序规则链, 按优先级首条命中、未命中放行, SQL 动态增删、即时生效、重启保留, 恒强制生效;独立服务形态) |
不是浅层字符串截取, 而是深入数据处理内核 —— 功能完整度与内存效率兼得。
| JSON 能力 | YoungsData | MySQL 8 | PostgreSQL | Oracle | SQL Server |
|---|---|---|---|---|---|
| 路径提取 EXTRACT / VALUE / QUERY | ✓ | ✓ | ✓ | ✓ | ✓ |
| -> / ->> 箭头语法 | ✓ | ✓ | ✓ | — | — |
| 存在 / 包含判定 EXISTS / CONTAINS | ✓ | ✓ | ✓ | ✓ | ◐ |
| 类型 / 长度 / 键 / 深度内省 | ✓ | ✓ | ✓ | ◐ | ◐ |
| 构造 JSON_OBJECT / ARRAY | ✓ | ✓ | ✓ | ✓ | ✓ |
| 按需精准提取、恒定内存占用 | 领先 | ◐ | ◐ | ◐ | ◐ |
| 跨方言 JSON 函数名兼容 | 独有 | — | — | — | — |
一个引擎, 听懂四种「方言」 —— 天然的 SQL 迁移友好层,MySQL / Oracle / PG / SQL Server 的存量 SQL 大量可直接运行。
函数同义词归一
GROUP_CONCAT = LISTAGG = STRING_AGG、NVL = IFNULL、DATEADD = DATE_ADD …… 数十组自动归一。
Oracle 格式化族
TO_CHAR / TO_DATE / TO_TIMESTAMP / TO_NUMBER 完整格式 token,Oracle 存量逻辑平滑迁入。
大小写不敏感
标识符大小写无关, 贴合多数库习惯, 迁移免去大量改写。
列名智能纠错
列名拼错时给出最相近列名建议, 开发调试体验友好。
铁律(2026-07-13 拍板): 各库互不冲突的语法/函数 —— MySQL 的 SUBSTRING_INDEX、PG 的 SPLIT_PART、Oracle 的 NVL —— 一律兼容并包、同时全支持, 与方言开关无关; 只有下面这些真语义冲突的极少数点, 才在求值处读 ctx.session.dialect() 分流。默认 MySQL 8(历史行为零迁移), 可用 -Dyoungsdata.sql.dialect=mysql|postgresql|oracle 或 SessionContext.withDialect(...) 切换。
| 真语义冲突点 | MySQL 8 (默认) | PostgreSQL 16 | Oracle 19c |
|---|---|---|---|
除零 / 模零 1/0 | 返 NULL | 抛除零错 | 抛除零错 |
GREATEST/LEAST 含 NULL | NULL 传染整体 | 忽略 NULL 取非空极值 | NULL 传染整体 |
SUBSTRING 负起点 | 从尾往回数 | 数轴语义 (起点 clamp 到 1) | 从尾往回数 |
LEFT/RIGHT 负计数 | 返空串 | 反向裁剪 (去掉尾/头 n 个) | 返空串 |
TRIM 多字符裁剪集 | 整串成组反复剥除 | 码点集合逐字符裁 | 多字符直接报错 (对齐 ORA-30001) |
REGEXP_REPLACE 默认范围 | 全部替换 · 反引用 $N | 只换首处 · 反引用 \N · 第 4 参是 flags | 全部替换 · 反引用 \N · 第 4 参是 pos |
裸 STDDEV/VARIANCE | 总体 (POP) | 样本 (SAMP) | 样本 (SAMP) |
EXTRACT(EPOCH FROM ts) | 按会话时区取真瞬时秒 | naive 时戳 as-if-UTC | 按会话时区取真瞬时秒 |
└─ 表中只是真冲突点; 除此以外的跨库函数与语法始终全部可用, 方言开关不影响它们。
① 各库语法糖自动吸收
各数据库特有写法自动认得并归一, 且只作用于语法本身、不碰字符串字面量里的用户数据: 反引号标识符、a<=>b NULL 安全等值、::type 转换、VARCHAR2 / NUMBER(p,s) 类型、-> / ->> JSON 取值、紧凑 INTERVAL、0x 十六进制…… 存量 SQL 原样直跑。
② 工业级解析内核
成熟稳定的 SQL 解析内核, 标识符大小写不敏感、贴合各库习惯; 绝大多数语法解析与方言无关, 一套语句三库通吃。
③ 名字归一 · 一实现吃多库别名
240+ 标量别名 + 聚合别名映射到同一内置实现: LCASE/UCASE→LOWER/UPPER、STRPOS/LOCATE→INSTR、NVL/IFNULL、GROUP_CONCAT=LISTAGG=STRING_AGG、DATE_PART→EXTRACT…… 与 SQL / Fluent 走同一张名字表。
④ 仅冲突处按方言
只有上表那些真语义冲突点才按当前方言「跟随谁」, 且对性能几乎零影响; 其余语法一视同仁, 切换方言不重写、不折损速度。
Oracle 数字/日期格式引擎
TO_CHAR / TO_NUMBER / TO_DATE / TO_TIMESTAMP 完整 format-token 双向引擎, Oracle 存量格式化逻辑平滑迁入。
类型别名跨库归一
NUMBER/NUMERIC/DEC→DECIMAL、VARCHAR2/CLOB/TEXT/STRING→VARCHAR、INT8/BIGINT→BIGINT、BINARY_DOUBLE/FLOAT8→DOUBLE…… 与方言设置无关一律接住。
UPSERT 双风格
MySQL ON DUPLICATE KEY UPDATE(含 VALUES(col))与 PostgreSQL ON CONFLICT ... DO UPDATE/DO NOTHING(含 EXCLUDED.col)双方言同引擎。
-- 同一段跨库函数, 三方言下始终可用(互不冲突, 兼容并包)
SELECT NVL(name, '匿名'), -- Oracle 写法
IFNULL(phone, '-'), -- MySQL 写法
SPLIT_PART(email, '@', 2), -- PostgreSQL 写法
SUBSTRING_INDEX(path, '/', -1) -- MySQL 写法
FROM users;
-- 仅真冲突点跟随方言: 下句 1/0 在 MySQL 返 NULL, 在 PG/Oracle 报错
-- java -Dyoungsdata.sql.dialect=postgresql ...
SELECT amount / NULLIF(qty, 0) FROM orders;
不是「只读分析玩具」—— 内置完整的事务写入引擎: 跨表原子提交、断电不丢、崩溃后自动恢复到一致状态; 外加唯一/联合索引、双方言 UPSERT、变更订阅(CDC)与时间点恢复(PITR),分析与在线写入一体。
读已提交 + 读己写
事务内立即看得到自己未提交的写、外部看不到; 跨表跨语句一致, 天然规避更新过程中的自读乱序。
崩溃 all-or-nothing
断电 / 宕机重启后自动恢复: 每个事务要么整个生效、要么整个没发生, 绝不会留下「半个事务」; 未提交的自动回滚、对数据零残留。
commit() · 同步零丢失
调用返回即代表已安全落盘, 此后任意断电都不会丢本事务。最强持久档, 转账等零容忍场景首选。
commitAsync() · 异步 ≤10ms
返回极快、写入吞吐更高; 进程被杀零丢失, 真断电最多丢约 10ms(且以整事务为单位, 绝不半批)。默认持久档。
| 持久化档 | 断电丢失窗口 | 说明 |
|---|---|---|
YoungsData · commit() 同步 | 0 · 断电零丢失 | 返回即已落盘, 最强持久, 与各库最严档同级 |
YoungsData · commitAsync() 异步(默认) | ≤ 10ms | 吞吐优先; 进程被杀零丢失, 断电最多丢约一个组提交周期 |
| MongoDB · 默认日志刷盘 | 约 100ms | journal 默认刷盘间隔约 100ms |
| MySQL · 放宽持久化(flush_log=2/0) | 约 1s | 约每秒刷一次盘 |
└─ 对比的是「吞吐优先 / 异步持久」这一档: YoungsData 异步窗口 ≤10ms, 比 MongoDB 约 100ms、MySQL 放宽档约 1s 紧一个数量级; 要绝对零丢失随时切 commit()(与各库最严档同级)。
唯一 / 联合 / 主键索引
CREATE [UNIQUE] INDEX 建唯一或普通索引、支持多列联合; 点查 / 等值 / 前缀 / 范围 / IN 自动命中。索引只加速、永不影响正确性。
UPSERT 双方言
MySQL ON DUPLICATE KEY UPDATE 与 PostgreSQL ON CONFLICT ... DO UPDATE / DO NOTHING 同引擎, 存量写入逻辑平滑迁入。
变更订阅 · 内建 CDC
可订阅已提交的行级变更做增量同步 / 物化视图 / 审计, 进程内、跨进程两种消费方式; 只交付已安全落盘的数据, 崩溃会回滚的绝不外流。
时间点恢复 PITR
周期快照 + 命令行还原到「近 N 天内任意时刻」, 整库或单表级, 原库零接触; 还原结果按事务批生效, 绝不含半个事务。
| 维度 | YoungsData |
|---|---|
| 隔离级别 | 读已提交 + 读己写(事务内自见其写, 跨语句一致) |
| 原子性 | 跨表跨多行事务原子提交; 单条多行 DML 亦为一个原子批 |
| 持久化 | commit() 同步零丢失 / commitAsync() 异步 ≤10ms(默认)两档可选 |
| 崩溃恢复 | 重启自动恢复到一致态; all-or-nothing, 绝无半事务 |
| 索引 | 主键、唯一、普通、多列联合; 自动命中点查 / 范围 / IN |
| UPSERT | MySQL ON DUPLICATE KEY · PostgreSQL ON CONFLICT 双方言 |
| CDC / 变更订阅 | 已提交行级变更可订阅, 进程内 + 跨进程消费, 至少一次投递 |
| 时间点恢复 | 周期快照 + 还原到任意时刻(整库 / 表级, 原库零接触) |
| 按天历史 | 改删旧版本自动按天留存, 全链可正 / 倒序回放 |
| 大规模 | 支持按主键分片写入; 超大事务自动落临时盘, 不撑爆内存 |
| 诚实边界 | SAVEPOINT / 跨会话快照隔离 / Serializable 暂不支持 —— 明确报错, 不静默兜底 |
SqlEngine eng = SqlEngine.builder(dir).transactional().build(); // 开事务档
SqlQuery sql = eng.sql();
// ① 声明式(首选): 正常返回自动 commit(同步落盘), 抛异常自动回滚, 冲突自动重试
sql.transact(txn -> {
txn.executeUpdate("UPDATE account SET bal = bal - 100 WHERE id = ?", 1);
txn.executeUpdate("UPDATE account SET bal = bal + 100 WHERE id = ?", 2);
}); // 跨两表原子; 高并发下总额精确守恒、无扣穿
// ② 命令式: try-with-resources, 忘 commit 自动回滚, 事务内 SELECT 读己写
try (SqlTxn txn = sql.begin()) {
txn.executeInsert("INSERT INTO audit (id, note) VALUES (10, 'transfer')");
Object[] mine = txn.one("SELECT note FROM audit WHERE id = 10"); // 立即可见
txn.commit(); // 同步落盘: 返回即断电不丢
}
// 同步 vs 异步 —— 按业务对"丢失容忍度"选一档
txn.commit(); // 零丢失: 返回即已落盘, 转账/账务等零容忍场景
txn.commitAsync(); // 高吞吐: 返回快, 进程被杀零丢失, 断电最多丢约 10ms(埋点/流水)
-- UPSERT 双方言 + 唯一 / 普通索引
INSERT INTO up (id, n, v) VALUES (1, 77, 'd') ON CONFLICT (id) DO UPDATE SET n = EXCLUDED.n; -- PG
INSERT INTO up (id, n, v) VALUES (1, 5, 'x') ON DUPLICATE KEY UPDATE n = VALUES(n); -- MySQL
CREATE UNIQUE INDEX uq_email ON users (email);
CREATE INDEX ix_city ON users (city, level); -- 多列联合
一套「装配入口 SqlEngine + 双门面(SQL 文本 SqlQuery / Fluent no-SQL Db+D)+ 预编译 Prepared* + 三态消费(list / forEach / cursor)+ RPC 电报 spec」的完整编程式 API —— 每个入口都是一行一签名、可直接抄用。
SqlEngine 一站装配
builder(dir).build() 一次接好元数据 + 存储 + 查询门面; AutoCloseable 自动 flush 落盘。Builder 链式开关: 事务 / 索引 / 分表 / 历史 / 方言 / 只读 / 自带存储工厂。
SqlQuery · SQL 文本门面
查询三态 list / forEach / cursor + first / one / exists / count; DML executeInsert/Update/Delete; DDL executeDdl; 统一入口 execute(sql) 自动分派; detectKind(sql) 词法级 ~10ns 识别语句类型。
预编译 Prepared*
prepare / prepareInsert/Update/Delete: 一次解析优化, 后续只绑参执行。真 batch 比循环快 5-50×; DDL 改表后自动重编译, 无需手动 re-prepare。
Db + D · Fluent no-SQL
不写 SQL 字符串也能建表/增删改查: db.select(...).list(), 与 SQL 门面 1:1 语义、零解析开销、节点不可变线程安全。
Cursor 拉取式游标
while ((row = c.next()) != null) 惰性读一行、零缓冲最省内存; EOF 自动 close 不泄漏句柄。与 list/forEach 并列的第三种消费态。
SessionContext 会话
不可变可派生: 时区 / Locale / MathContext / castMode / batchSize / spill 预算 / 审计 / 方言…… withDialect / withCastMode / ... 链式派生, 环境敏感参数集中管控。
存储 SPI · 自定义数据源
ReaderFactory / WriterFactory / RowSink 开放接入: 行存、内存、Kafka、RPC 虚拟源皆可; 谓词下推经 ReaderContext.pushedOperators 传入, IndexInfo 驱动点查/范围决策。
RPC 电报 · spec 包
瘦客户端(仅 D + spec)组装 QuerySpec → byte[] 上线 → 服务端 Db.fromSpec 还原执行; 同电报再来零解码零重编译, 客户端看不到任何解析/执行类。
try (SqlEngine engine = SqlEngine.builder(new File("/data/mydb")).build()) {
SqlQuery sql = engine.sql();
sql.executeDdl("CREATE TABLE orders (id BIGINT PRIMARY KEY, customer VARCHAR(64), amount DECIMAL(18,2))");
sql.executeInsert("INSERT INTO orders (id, customer, amount) VALUES (1, 'Alice', 12.50)");
Object[] one = sql.one ("SELECT customer FROM orders WHERE id = 1");
List<Object[]> rows = sql.list("SELECT id, amount FROM orders WHERE amount > 1000");
} // close() 自动 flush 落盘 + 关 reader/writer 池
PreparedSqlQuery pq = sql.prepare("SELECT id FROM orders WHERE amount > ? AND status = ? LIMIT ?");
List<Object[]> r1 = pq.list(5000L, "PAID", 10L);
List<Object[]> r2 = pq.list(8000L, "PAID", 20L); // 零 plan 开销
// 真 batch: 一批共享 1 次 borrow + flush, 大批量远快于循环 execute
PreparedInsertSqlQuery ins = sql.prepareInsert("INSERT INTO orders (id, customer, amount) VALUES (?, ?, ?)");
ins.executeBatch(List.of(new Object[]{1L,"A",10.0}, new Object[]{2L,"B",20.0}));
// 拉取式游标: 最省内存, EOF 自动 close
try (Cursor c = sql.cursor("SELECT id, amount FROM orders WHERE amount > 100")) {
Object[] row;
while ((row = c.next()) != null) process(row);
}
Db db = engine.db();
db.createTable("orders").column("id", Db.Type.BIGINT).pk()
.column("customer", Db.Type.VARCHAR, 64).column("amount", Db.Type.DECIMAL, 18, 2).execute();
List<Object[]> r = db.select(col("o.id"), col("c.name"), sum(col("o.amount")).as("total"))
.from("orders", "o")
.join("customers", "c").on(eq(col("o.cust_id"), col("c.id"))) // JOIN 必须 .on/.using 收尾
.where(gt("o.amount", 0L))
.groupBy(col("o.id"), col("c.name"))
.having(gt(col("total"), val(1000)))
.orderBy(desc("total")).limit(20).list();
// RPC 电报: 客户端组装 → byte[] → 服务端还原执行
QuerySpec spec = QuerySpec.select(col("id"), col("amount")).from("orders").where(gt("amount", 1000L)).limit(10);
byte[] wire = SpecCodec.DEFAULT.encode(spec); // 上线
List<Object[]> back = db.fromSpec(wire).list(); // 服务端 Db.fromSpec 直达车
// 纯 SELECT(+ 可选 WHERE/LIMIT)可走直通路径: 跳过装箱拆箱, 每行不再新建 Object[]
if (sql.canRunRow(q)) {
List<Order> os = sql.row().list(
r -> new Order((Long) r.get(0), (String) r.get(1)), // r 是 RowRec, get(i) 取值
q);
}
// 预编译 + 直通 + 映射, OLTP 小查询最省; 直通视图同样有 list / forEach / first / one / batch
PreparedSqlQuery pq = sql.prepare("SELECT id, name FROM users WHERE city = ?");
List<Object[]> rows = pq.row().list("hangzhou");
└─ RowRec.get(i) 按表字段序取值; 需要按列名 / 类型化取值时用通用 Object[] 路径。计数 / 存在性用 count(sql) / exists(sql)。
SqlQuery(SQL 文本)、Db+D(Fluent)、spec(RPC 电报)三种写法语义完全一致、可混用, 事务与 journal 行为不变。SQL 有编译缓存, Fluent 零解析, RPC 电报按字节 LRU 复用 —— 按团队习惯与调用场景自由选。以「参数化预编译从结构上根除 SQL 注入」为核心, 叠加写入值强校验、严格类型规则、扫描审计与内存预算守护, 形成一条 fail-loud 的纵深防线 —— 脏数据不入库、错答不返回、恶意查询打不爆内存。
参数化预编译 · 结构级免疫注入
? 参数在执行期只作为一个已类型化的值参与比较, 从不被拼回 SQL 文本、也从不重新解析 —— 再恶意的字符串实参也无法改变 SQL 结构。防注入靠数据与代码彻底分离, 而非转义黑名单。
占位符仅限「值位」
? 只能出现在 WHERE / SELECT / IN / HAVING / CASE 的值端; 字段名、表名、函数名、LIMIT/OFFSET 一律不接受参数 —— 从语法层杜绝「用参数拼出表名 / 关键字」这类变体。
写入值强校验 · 脏数据挡在库外
每行每列入库前强类型校验: VARCHAR 超长、DECIMAL 丢精度 / 超范围、整数越界一律报错而非静默截断 / 饱和; 非法类型的值直接拒收, 保证存储层约束不被绕过。
严格类型规则 · 拒绝静默错答
布尔 / 字符串禁入算术、CAST 溢出 / 未知目标类型一律报错, 废止「把 "abc" 悄悄当 0」这类静默兜底; 批量导入可切容错档(TRY_CAST 语义)让失败返 NULL。
扫描审计 + 可观测
可开启断言式审计: 同一物理表被重复扫描即报错(附违规表名); 也可挂被动观测钩子, 生产环境持续上报各表实际扫描次数。既护性能也护一致性。
内存预算守护 · 防 OOM / DoS
必须整体驻留的形态(子查询结果、右表缓冲、窗口分区、GROUP_CONCAT、分位数…)超预算即清晰报错而非静默 OOM; 能溢写到盘的算子内存优先、绝不打爆进程。
错误分级 · 可安全对外
SQL 层错误分「不支持」(可返 501)与「用户写错」(可返 400)两类, 供业务分流 HTTP 码; 错误消息受控、结构化, 不泄漏内部堆栈。
结果放大守护 + 最小攻击面
REPEAT / LPAD / RPAD 等结果超上限即报错(防「小输入撑出巨大结果」); 高风险复杂 DML(子查询删改、跨表写)默认关闭并给安全改写建议, 确需时才显式开启。
// ✅ 安全: ? 只当"值"比较, 永不改变 SQL 结构
PreparedSqlQuery pq = sql.prepare("SELECT id, amount FROM orders WHERE status = ? AND amount > ?");
List<Object[]> rows = pq.list(userInput, threshold); // userInput 即使是 x' OR '1'='1 也只是一个字符串常量
// ✗ 危险: 把用户输入拼进 SQL 文本 —— 结构可被篡改(应改用上面的 ? 绑定)
// String sql = "SELECT ... WHERE status = '" + userInput + "'";
// 参数个数不匹配 / 未绑定 → 立即报错, 而非静默当 NULL 返回空集(比报错更危险)
pq.list(1); // 抛异常: 参数数量不匹配(SQL 有 2 个 ?, 传入 1)
// 类型 / 审计 / 内存, 都是可选开的会话级开关(零开销)
SessionContext s = SessionContext.defaults()
.withCastMode(CastMode.NULL_ON_ERROR) // 批量导入: CAST 失败返 NULL 而非抛错
.withAuditSingleScan(true) // 断言: 同表被重复扫描即报错
.withScanObserver(t -> metrics.mark(t)) // 监控: 上报每个物理表扫描次数
.withSpillBudgetBytes(512L << 20); // 内存预算: 超限清晰报错而非 OOM
-- 账号与授权: MySQL 8 风格 DCL, 8 个权限动词 × 全局 / 库 / 表三级作用域(ADMIN 仅全局); 本组语句均需管理员身份执行
CREATE USER etl IDENTIFIED BY 'Str0ng!Pass';
GRANT SELECT, INSERT ON sales.* TO etl; -- 表 / 库级拦截默认观察模式(违规只记审计日志), 确认无误后由服务端开关升为强制
SHOW GRANTS FOR etl;
-- 来源访问规则: 「账号 × 网段」有序规则链(对齐 PostgreSQL pg_hba 模型), SQL 动态增删、即时生效、重启保留、恒强制
CREATE ACCESS RULE r_etl ALLOW etl FROM '10.20.3.0/24' PRIORITY 10; -- etl 仅允许从专属网段登录
CREATE ACCESS RULE r_etl_close REJECT etl FROM ALL PRIORITY 20; -- etl 其余来源一律拒(其他账号不受影响)
CREATE ACCESS RULE r_default REJECT ALL FROM ALL PRIORITY 9999; -- 收口: 未命中任何规则默认放行, 「名单外全拒」须显式加这条
SHOW ACCESS RULES; -- 输出可回放语句, 行序即求值序(按 PRIORITY 升序首条命中)
DROP ACCESS RULE r_etl_close; -- 删除即刻生效, 只影响新连接; 本机 loopback 恒放行防误配自锁
不靠「分布式堆机器」, 而把单机的每一滴性能榨干、把内存鲁棒性做到极致。
读取路径相对值(对照同机「手写极致优化直读」基准):比值越低越快,<1 即代表反超手写直读。
海量数据永不 OOM
全算子弹性内存自适应:内存不够先溢写本地盘续跑, 绝不 OOM、不拖垮宿主进程。
智能扫描复用
自关联、多次引用同表自动共享同一次扫描, 杜绝重复 I/O。
智能数据裁剪
过滤与列裁剪在源头一次完成, 只读必要的行与列, 从源头减负。
Top-N 极速直取
ORDER BY + LIMIT 用极速直取, 深翻页不爆内存。
预编译多级缓存
编译产物多级缓存, 高 QPS 在线查询 CPU 开销趋零。
毫秒级启动
嵌入式无集群、无调度, 进程内启动即查询, 中等数据亚秒响应。
弹性水平扩展
服务化为分布式计算节点, 按数据规模横向加机, 吞吐随节点近线性增长。
海量智能分治
海量数据自动分治, 精准定位只读命中片区, 分而治之、线性提速。
| 维度 | YoungsData | 传统单机 DB MySQL / PG |
列存向量化 DuckDB / ClickHouse |
分布式 Spark / Hive |
|---|---|---|---|---|
| 启动 / 查询延迟 | 极低 | 低 | 低 | 高(集群调度) |
| 内存鲁棒性 | 永不 OOM | 磁盘临时表硬扛 | 部分支持 | 弹性内存自适应 |
| 复杂分析 SQL | 第一梯队 | 中 | 强 | 强 |
| 中等数据延迟 | 极优 | 优 | 优 | 差(stage 开销) |
| 嵌入 / 集成成本 | 极低(单 jar) | 中 | 低 | 高 |
注:即便以 out-of-core 著称的 DuckDB, 官方文档至今仍专设 OOM 排查指南(部分聚合 / PIVOT / 多阻塞算子组合场景);YoungsData 将全部物化算子纳入弹性内存自适应, 无豁免算子。
复杂报表、跨源融合、多结果集合并 —— 一条 SQL 搞定, 无需在应用层来回搬运数据。
多结果集合并
UNION / UNION ALL 单次扫描融合,INTERSECT / EXCEPT 排序归并 —— 多张报表结果一次性合并去重。
公共子结果复用
多次引用的 CTE 只计算一次、多处共享, 复杂报表的公共中间结果不重复跑。
多表 / 多源关联
多表 JOIN、自关联、派生表任意一侧, 关系数据与 JSON 列混合关联, 跨源数据一查到底。
行列转置报表
PIVOT / UNPIVOT 原生支持, 交叉报表、宽窄表互转一条 SQL 完成。
多维汇总
ROLLUP / CUBE / GROUPING SETS 一次出多层小计与总计, 经营报表的「合计行」自动生成。
窗口分析直出
窗口函数全集 + GROUPS 帧 + QUALIFY:同比环比、累计、移动平均、分组 Top-N, 一句直出。
海量去重 · 近似估算
精确去重 + HLL 近似去重计数, 亿级基数秒级估算, 大报表统计不卡。
海量不爆内存
合并 / 聚合 / 排序全链路可弹性内存自适应, 单机内存放不下的大报表也能稳稳跑完。
一套自研引擎内核, 既能单机极致、又有自研的分布式数据处理逻辑。业务只需描述「表是什么、数据在哪」, 引擎负责 SQL 全栈 —— 按数据规模自由伸缩, 小到嵌入边缘设备, 大到分布式计算集群节点。
开放数据源接入
不绑定任何特定存储:业务实现标准接入层即可对接任意存储 / 文件格式 / 分库分表, 引擎负责其上全部 SQL 解析、优化、执行 —— 灵活扩展的根基。
单机极致形态
嵌入式单 jar、零集群依赖、毫秒级启动;内存放不下也永不 OOM, 中等规模数据亚秒响应。
自研分布式数据处理
内建海量数据智能分治, 自动定位并只读取命中的数据片区 —— 海量数据分而治之的处理逻辑由引擎自研掌控。
横向扩展为计算节点
纯自包含、无外部依赖的执行内核, 可服务化(RPC)并作为分布式架构中的高性能计算单元横向扩展 —— 单点越强, 集群越强。
| 形态 | 状态 | 能力与场景 |
|---|---|---|
| 嵌入式 (JDBC · 单 jar) | ✓ 现已支持 | 业务进程内分析、边缘计算、桌面分析, 零网络开销、最低延迟 |
| 独立服务 (RPC Server) | ✓ 现已支持 | 多客户端共享、集中数据服务 |
| 分布式数据处理 (智能分治) | ✓ 架构已支持 | 海量数据智能分治 + 源头精准读取, 单机内核作为分布式计算节点横向扩展 |
| MPP 统一编排 | ◐ 持续演进 | 多计算节点统一调度, 突破单机数据规模上限 |
SQL 表达不了的业务逻辑(脱敏加密、用户轨迹、自定义统计……), 写个普通 Java 方法就能变成 SQL 函数、在查询里直接调。三种类型全覆盖, 每种都能一次返回多列 —— 简单到「一个注解 + 一个方法」。
传统数据库(MySQL / PostgreSQL 等)给不了这样的体验:存储过程语言受限, 能跑任意 Java 逻辑、引第三方库、还能热替换的方案在工程上几乎不存在, 自定义聚合更无从谈起。
YoungsData 用纯 Java 提供三种自定义函数 —— 单行(SCALAR)逐行变换、分组(GROUP)对整组数据 holistic 计算、流式(STREAMING)逐行累加, 三种都能一次产出多列结果;而对标 Spark / Flink, 我们在「怎么写、怎么配、怎么改」上简单一个量级 —— 尤其分组 / 流式聚合, 它们要继承 UDAF / AggregateFunction、实现一大堆方法和 schema, 我们只要一个方法。
而真正把这套 UDF 推上量级的, 是每种函数都能接一个 UdfState s —— 一个 put / get 的状态对象(就是下面例 ③ 里那个 stats,get 还能直接强转类型)。它不止在行与行、组与组之间保存中间量,更能在 SQL 执行时接住你从查询里传入的自定义参数 —— 漏斗的步骤定义、路径的规则、留存的时间窗、各种阈值……函数里一句 s.get(...) 取出来即用。于是同一个 UDF 不再写死, 而是按查询参数化:SQL 负责把海量数据筛出来,UdfState 负责带着「参数 + 状态」边扫边算。
单行 SCALAR
逐行字段变换(加密、脱敏、格式化)。一个 Object f(UdfRow), 可一次返回多列(如密文 + 掩码)。对应 Spark UDF / Flink ScalarFunction。
分组 GROUP
对整组有序数据做 holistic 计算(用户轨迹、漏斗、留存)。引擎直接喂你整组行的迭代器, 一次遍历搞定, 返回多列。对应 Spark UDAF —— 它要继承 Aggregator, 实现 6 个成员(4 方法 + 2 Encoder)。
流式 STREAMING
逐行累加(自定义统计、状态机)。就一个任意名方法:逐行更新中间量、组末用 end 产出, 一次算出 min/max/avg 等多列。对应 Flink AggregateFunction —— 它要 3 个方法 + 自管 Accumulator 类。
jar 丢目录 · 热加载
打成 jar 放进目录即自动注册,SQL 立即可调;改完换个 jar 热更新、不停服。Spark / Flink 改函数要重新打包、重新提交集群作业。
三种类型 · 易用度对比
| 维度 | YoungsData | 传统库 MySQL / PG |
Spark | Flink |
|---|---|---|---|---|
| 单行函数(标量) | @Udf + 1 方法 | — | 继承 UDF1…22 或 functions.udf() |
继承 ScalarFunction 重写 eval() |
| 分组 / 聚合函数 | 1 方法 游标直给 | — | 继承 Aggregator 6 方法 + 2 Encoder 类型定义 |
继承 AggregateFunction 3 方法 + Accumulator POJO 类 |
| 流式累加函数 | 1 方法 逐行+end | — | 同 Aggregator 无专用 API · 仍需同等 6 方法 |
同 AggregateFunction 自管 Accumulator · 组级内存 |
| 一函数返多列 | returns 声明 | — | 构造 StructType + RowFactory 需手动 .getField() 拆列 |
返 Row + TypeInformation 需额外定义列类型 schema |
| 注册方式 | jar 丢目录自动 | — | spark.udf.register() 代码注册 · 修改需重新提交作业 |
env.createTemporaryFunction() 代码注册 · 修改需重启 Job |
| 热更新(不重启) | ✓ | — | — 重新打包 + 提交集群作业 |
— 重启 Flink 作业才能加载 |
注:对比的是「自定义函数」的写法与二次开发体验, 非整体引擎定位。
一眼看懂:三种类型, 各省多少代码
import org.apache.spark.sql.*;
import org.apache.spark.sql.types.*;
StructType out = new StructType()
.add("cipher", DataTypes.StringType)
.add("masked", DataTypes.StringType);
UDF1<String, Row> enc = phone -> {
if (phone == null) return null; // null 也得自己判
return RowFactory.create(
aesEncrypt(phone), // 密文
phone.substring(0, 3) + "****" + phone.substring(7)); // 掩码
};
spark.udf().register("encrypt", enc, out);
// 调用还得 .getField 拆列:
// SELECT encrypt(phone).cipher, encrypt(phone).masked …
@Udf(name = "ENCRYPT",
type = UdfKind.SCALAR,
returns = { @Col(name = "cipher", type = UdfType.STRING),
@Col(name = "masked", type = UdfType.STRING) })
public static Object encrypt(UdfRow row) {
String phone = (String) row.value();
if (phone == null) { return null; }
return new Object[] {
aesEncrypt(phone), // 密文
phone.substring(0, 3) + "****" + phone.substring(7) // 138****5678
};
}
省在哪:多列输出只需在 returns 里声明、方法 return Object[] —— 不必拼 StructType、不必 RowFactory、不必手工注册;列名直接就是声明的名字。
case class Buf(path: String, n: Int)
case class Out(path: String, stops: Int)
object UserPath extends Aggregator[Row, Buf, Out] {
def zero = Buf("", 0)
def reduce(b: Buf, r: Row) = { // 自己拼缓冲
val p = r.getString(0)
if (p == null) b // null 也得自己跳
else {
val sep = if (b.n > 0) " → " else ""
Buf(b.path + sep + p, b.n + 1)
}
}
def merge(a: Buf, b: Buf) = // 还要写 merge
Buf(a.path + " → " + b.path, a.n + b.n)
def finish(b: Buf) = Out(b.path, b.n)
def bufferEncoder = Encoders.product[Buf] // 还要两个 encoder
def outputEncoder = Encoders.product[Out]
}
spark.udf.register("user_path", functions.udaf(UserPath))
@Udf(name = "USER_PATH",
type = UdfKind.GROUP,
returns = { @Col(name = "stops", type = UdfType.INT),
@Col(name = "path", type = UdfType.STRING) })
public static Object userPath(Iterator<UdfRow> rows) {
int stops = 0;
StringBuilder path = new StringBuilder();
while (rows.hasNext()) {
String place = (String) rows.next().value();
if (place == null) { continue; }
if (stops++ > 0) { path.append(" → "); }
path.append(place);
}
return new Object[] {
stops, // 经停数
path.toString() // 路径
};
}
// 结果: stops = 3, path = "公司 → 食堂 → 健身房"
省在哪:Spark UDAF 要你手动维护缓冲、写 reduce / merge / 两个 encoder + 两个 case class;YoungsData 直接把整组有序行的迭代器交给你, 一次遍历就能算完、返回多列 —— 分组逻辑回归直觉。想算更复杂的(各时段去了哪、停留多久), 也只是在循环里多写几行, 甚至直接返回一段 JSON。大数据组内无序靠引擎内存优先+溢写, 不 OOM。
更进一步:方法签名加上 UdfState s, 把查询时传入的参数(路径规则、漏斗步骤、停留阈值)用 s.get 取出来 —— 同一个分组函数就成了可配置的路径 / 漏斗分析算子, 换组参数即换一种口径。
public static class Acc {
double min = Double.MAX_VALUE, max = -Double.MAX_VALUE;
double sum = 0;
long cnt = 0;
}
public class StreamStats extends AggregateFunction<Row, Acc> {
public Acc createAccumulator() { return new Acc(); }
public void accumulate(Acc a, Double v) {
if (v == null) return; // null 也得自己判
a.min = Math.min(a.min, v);
a.max = Math.max(a.max, v);
a.sum += v;
a.cnt++;
}
public Row getValue(Acc a) {
if (a.cnt == 0) return null; // 空组也得自己处理
return Row.of(a.min, a.max, a.sum / a.cnt);
}
public TypeInformation<Row> getResultType() {
// 还得手定义 3 列类型
return Types.ROW(Types.DOUBLE, Types.DOUBLE, Types.DOUBLE);
}
}
tableEnv.createTemporarySystemFunction("stream_stats", StreamStats.class);
@Udf(name = "STREAM_STATS",
type = UdfKind.STREAMING,
returns = { @Col(name = "min_v", type = UdfType.DOUBLE),
@Col(name = "max_v", type = UdfType.DOUBLE),
@Col(name = "avg_v", type = UdfType.DOUBLE) })
// s = 状态袋:put / get(get 可强转)。既存逐行中间量,也能接住 SQL 查询时传入的参数
public static Object stats(UdfState s, UdfRow row, boolean end) {
if (end) { // 组末:产出一次
long n = s.getLong("cnt", 0);
if (n == 0) { return null; } // 空组 → 各列 NULL
return new Object[] {
s.getDouble("min", 0), // min
s.getDouble("max", 0), // max
s.getDouble("sum", 0) / n // avg
};
}
Double v = (Double) row.value(); // 逐行:只累加
if (v == null) { return null; }
s.put("min", Math.min(s.getDouble("min", v), v));
s.put("max", Math.max(s.getDouble("max", v), v));
s.put("sum", s.getDouble("sum", 0) + v);
s.put("cnt", s.getLong("cnt", 0) + 1);
return null;
}
省在哪:没有 Accumulator 类、没有固定的 finish —— 就一个 @Udf 标注的任意名方法:end=false 逐行只累加中间量、end=true 组末产出一次(单列直接 return 值、多列 return Object[])。增量聚合 O(state) 内存, 海量数据逐行流过、不缓冲整组、不 OOM。
再加一层:把查询参数(阈值、时间窗、漏斗步骤……)在执行时传进来, 函数里 s.get 取出来,同一个 STREAMING 函数就能按查询算不同的统计 / 漏斗 / 状态机 —— 这正是「执行期传参」让 UDF 变无敌的地方。
从嵌入式分析到 AI 生成 SQL 的执行底座,YoungsData 覆盖七大典型落地场景。
嵌入式分析 / 边缘计算
单 jar 嵌入,零集群,毫秒级启动。
复杂报表 / BI 后端
高阶分析语法齐全,一条 SQL 搞定复杂报表。
多源异构数据分析
关系 + JSON 混合查询,跨方言 SQL 直接跑。
在线低延迟查询服务
预编译缓存 + 点查早停,高 QPS 稳定低延迟。
中等规模大数据
内存放不下也不怕,弹性内存永不 OOM,本地盘扛住。
存量 SQL 迁移
听懂四种方言,存量报表 SQL 绝大多数原样直跑。
AI 生成 SQL 的执行底座
大模型生成 SQL 常混用方言 —— 四方言归一直接听懂;不支持即明确报错 + 改写建议,Agent 自纠错闭环;单 jar 毫秒启动,天然的进程内 SQL 沙箱。
内置 140+ 函数,11 大类全覆盖。输入关键词即时检索, 签名 + 说明一目了然 —— 开发者与 AI 即学即用。同义词(如 NVL / IFNULL)均可直接书写。
| 函数 | 签名 | 说明 |
|---|---|---|
| UPPER | UPPER(str) | 转大写 |
| LOWER | LOWER(str) | 转小写 |
| TRIM | TRIM([BOTH/LEADING/TRAILING][c FROM] str) | 去除首尾(或指定字符集) |
| LTRIM | LTRIM(str) | 去除左侧空白 |
| RTRIM | RTRIM(str) | 去除右侧空白 |
| CONCAT | CONCAT(a, b, …) | 拼接, 忽略 NULL |
| CONCAT_WS | CONCAT_WS(sep, a, b, …) | 用分隔符连接, 跳过 NULL 值 |
| SUBSTRING / SUBSTR | SUBSTRING(str, start[, len]) | 截取子串,1-based 按码点 |
| LENGTH / CHAR_LENGTH | LENGTH(str) | 字符长度(码点数) |
| OCTET_LENGTH | OCTET_LENGTH(str) | UTF-8 字节长度 |
| BIT_LENGTH | BIT_LENGTH(str) | UTF-8 位长度 |
| REPLACE | REPLACE(str, from, to) | 子串全部替换 |
| LEFT | LEFT(str, n) | 取左 N 个字符 |
| RIGHT | RIGHT(str, n) | 取右 N 个字符 |
| LPAD | LPAD(str, len, pad) | 左侧填充至指定长度 |
| RPAD | RPAD(str, len, pad) | 右侧填充至指定长度 |
| POSITION / LOCATE | POSITION(sub IN str) | 子串位置(1-based, 未找到 0) |
| INSTR | INSTR(str, sub) | 子串首次位置(0=未找到) |
| INITCAP | INITCAP(str) | 每个单词首字母大写 |
| REVERSE | REVERSE(str) | 反转字符串 |
| REPEAT | REPEAT(str, n) | 重复 N 次(超 16M 字符报错防 OOM) |
| TRANSLATE | TRANSLATE(str, from, to) | 逐字符映射, 多余字符删除 |
| SPLIT_PART | SPLIT_PART(str, delim, n) | 取第 n 段(1-based, 越界空串) |
| SUBSTRING_INDEX | SUBSTRING_INDEX(str, delim, count) | 取前/后 count 段(负数取后) |
| FIND_IN_SET | FIND_IN_SET(s, csv) | s 在逗号集合中的 1-based 位置 |
| STARTSWITH | STARTSWITH(s, p) | 前缀判定 → 布尔 |
| ENDSWITH | ENDSWITH(s, p) | 后缀判定 → 布尔 |
| CONTAINS | CONTAINS(s, sub) | 子串包含判定 → 布尔 |
| ASCII | ASCII(str) | 首字符码点(空串 → NULL) |
| CHR | CHR(n) | 码点转字符 |
| LEVENSHTEIN | LEVENSHTEIN(a, b) | 编辑距离 |
| OVERLAY | OVERLAY(s PLACING r FROM start[ FOR len]) | 子串覆盖替换 |
| 函数 | 签名 | 说明 |
|---|---|---|
| ABS | ABS(x) | 绝对值 |
| ROUND | ROUND(x[, d]) | 四舍五入到 d 位 |
| CEIL / CEILING | CEIL(x) | 向上取整 |
| FLOOR | FLOOR(x) | 向下取整 |
| POWER / POW | POWER(base, exp) | 幂运算 |
| SQRT | SQRT(x) | 平方根 |
| CBRT | CBRT(x) | 立方根 |
| EXP | EXP(x) | 自然指数 e^x |
| LOG / LN | LOG([base,] x) / LN(x) | 对数(单参=自然对数, 双参=指定底) |
| LOG10 | LOG10(x) | 常用对数(底 10) |
| LOG2 | LOG2(x) | 二进制对数(底 2) |
| MOD | MOD(a, b) | 取模(b=0 → NULL) |
| SIGN | SIGN(x) | 符号 -1 / 0 / 1 |
| GREATEST | GREATEST(a, b, …) | 取最大(任一 NULL → NULL) |
| LEAST | LEAST(a, b, …) | 取最小(任一 NULL → NULL) |
| SIN / COS / TAN | SIN(x) / COS(x) / TAN(x) | 三角函数(弧度) |
| ASIN / ACOS / ATAN | ASIN(x) / ACOS(x) / ATAN(x) | 反三角函数 → 弧度 |
| ATAN2 | ATAN2(y, x) | 双参反正切 |
| PI | PI() | 圆周率常量 |
| RADIANS | RADIANS(deg) | 角度转弧度 |
| DEGREES | DEGREES(rad) | 弧度转角度 |
| TRUNCATE | TRUNCATE(x, d) | 向零截断到 d 位(不四舍五入) |
| TRUNC | TRUNC(num[, d]) / TRUNC(date[, unit]) | 数字截断 / 日期截断(按首参类型) |
| WIDTH_BUCKET | WIDTH_BUCKET(x, lo, hi, n) | 等宽分桶 → 1..n |
| RAND / RANDOM | RAND() | [0,1) 随机 double |
| GCD | GCD(a, b) | 最大公约数 |
| FACTORIAL | FACTORIAL(n) | 阶乘(n>20 溢出报错) |
| 函数 | 签名 | 说明 |
|---|---|---|
| YEAR | YEAR(dt) | 提取年 |
| MONTH | MONTH(dt) | 提取月 |
| DAY / DAYOFMONTH | DAY(dt) | 提取日 |
| HOUR | HOUR(dt) | 提取小时 |
| MINUTE | MINUTE(dt) | 提取分钟 |
| SECOND | SECOND(dt) | 提取秒 |
| QUARTER | QUARTER(dt) | 季度 1-4 |
| WEEK / WEEKOFYEAR | WEEK(dt) | 当年第几周 |
| DAYOFWEEK | DAYOFWEEK(dt) | 星期几(1=周日 … 7=周六) |
| DAYOFYEAR | DAYOFYEAR(dt) | 年内天序 1-366 |
| LAST_DAY | LAST_DAY(dt) | 当月最后一天 |
| NOW / CURRENT_TIMESTAMP | NOW() | 当前完整时间戳 |
| CURRENT_DATE | CURRENT_DATE | 今日 00:00:00(纯日期) |
| EXTRACT / DATE_PART | EXTRACT(field FROM dt) | 提取字段(支持 EPOCH/QUARTER 等) |
| DATE_FORMAT | DATE_FORMAT(dt, pattern) | 按 pattern 格式化 |
| DATE_ADD / DATEADD | DATE_ADD(dt, INTERVAL n UNIT) | 日期加 |
| DATE_SUB / DATESUB | DATE_SUB(dt, INTERVAL n UNIT) | 日期减 |
| DATEDIFF / DATE_DIFF | DATEDIFF(dt1, dt2) | 天数差 |
| DATE_TRUNC | DATE_TRUNC('unit', ts) | 日期截断到单位 |
| TIMESTAMPDIFF | TIMESTAMPDIFF(unit, d1, d2) | 跨单位整数差(向零截断) |
| TIMESTAMPADD | TIMESTAMPADD(unit, n, d) | 加 n 个 unit |
| ADD_MONTHS | ADD_MONTHS(d, n) | 加 n 个日历月(月末对齐) |
| MONTHS_BETWEEN | MONTHS_BETWEEN(d1, d2) | 月数差(小数) |
| CONVERT_TZ | CONVERT_TZ(ts, from_tz, to_tz) | 时区转换 |
| UNIX_TIMESTAMP | UNIX_TIMESTAMP([ts]) | epoch 秒(无参=当前) |
| FROM_UNIXTIME | FROM_UNIXTIME(sec) | epoch 秒 → 时间戳 |
| TO_CHAR | TO_CHAR(dt[, fmt]) | Oracle/PG 日期格式化 |
| TO_DATE / STR_TO_DATE | TO_DATE(text[, fmt]) | 解析为日期 |
| TO_TIMESTAMP | TO_TIMESTAMP(text[, fmt]) | 解析为时间戳(保留小数秒) |
| 函数 | 签名 | 说明 |
|---|---|---|
| BITAND | BITAND(a, b) | 按位与 |
| BITOR | BITOR(a, b) | 按位或 |
| BITXOR | BITXOR(a, b) | 按位异或 |
| BITNOT | BITNOT(a) | 按位取反 |
| SHIFTLEFT / LSHIFT | SHIFTLEFT(a, n) | 左移 |
| SHIFTRIGHT / RSHIFT | SHIFTRIGHT(a, n) | 算术右移 |
| 函数 | 签名 | 说明 |
|---|---|---|
| MD5 | MD5(str) | MD5 摘要(32 位十六进制) |
| SHA1 / SHA | SHA1(str) | SHA-1 摘要(40 位) |
| SHA256 / SHA2 | SHA256(str) | SHA-256 摘要(64 位) |
| SHA512 | SHA512(str) | SHA-512 摘要(128 位) |
| CRC32 | CRC32(str) | 无符号 32 位 CRC |
| TO_BASE64 | TO_BASE64(str) | Base64 编码 |
| FROM_BASE64 | FROM_BASE64(str) | Base64 解码 |
| HEX | HEX(n) | 整数转十六进制串 |
| BIN | BIN(n) | 整数转二进制串 |
| UUID | UUID() | 随机 UUID(36 位) |
| 函数 | 签名 | 说明 |
|---|---|---|
| COALESCE | COALESCE(a, b, …) | 返回首个非 NULL |
| IFNULL / NVL | IFNULL(a, default) | 空值替换 |
| NVL2 | NVL2(a, b, c) | a 非 NULL 返 b, 否则返 c |
| NULLIF | NULLIF(a, b) | a=b 返 NULL, 否则返 a |
| IF / IIF | IF(cond, then, else) | 简单三元条件 |
| 函数 | 签名 | 说明 |
|---|---|---|
| CAST | CAST(expr AS type) | 类型转换(INT/BIGINT/DOUBLE/DECIMAL/VARCHAR/DATE/TIME/TIMESTAMP …) |
| TRY_CAST / SAFE_CAST | TRY_CAST(expr AS type) | 安全转换, 失败返 NULL |
| TO_NUMBER | TO_NUMBER(text[, fmt]) | 按格式解析为 DECIMAL |
| TO_CHAR | TO_CHAR(num, fmt) | 数字格式化(与 TO_NUMBER 对称) |
| FORMAT | FORMAT(num, d) | 数字千分位定标度 / 日期格式化 |
| 函数 | 签名 | 说明 |
|---|---|---|
| REGEXP_REPLACE | REGEXP_REPLACE(str, pattern, repl) | 正则替换(全部匹配段) |
| REGEXP_EXTRACT / REGEXP_SUBSTR | REGEXP_EXTRACT(str, pattern[, group]) | 提取首次匹配的第 group 组(默认整体) |
| REGEXP_COUNT | REGEXP_COUNT(str, pattern) | 不重叠匹配个数 |
| 函数 | 签名 | 说明 |
|---|---|---|
| JSON_EXTRACT | JSON_EXTRACT(json, '$.a.b') | 按路径提取文本(亦为 -> 目标) |
| JSON_VALUE | JSON_VALUE(json, '$.a') | 提取标量并去引号(亦为 ->> 目标) |
| JSON_QUERY | JSON_QUERY(json, '$.a') | 仅返对象 / 数组 |
| JSON_UNQUOTE | JSON_UNQUOTE('"abc"') | 去最外层 JSON 引号 |
| JSON_EXISTS | JSON_EXISTS(json, '$.a') | 路径存在判定 → 布尔 |
| JSON_CONTAINS | JSON_CONTAINS(json, cand[, '$.path']) | 包含判定(结构递归比对) |
| JSON_VALID | JSON_VALID(json) | 合法 JSON 判定 → 布尔 |
| JSON_TYPE | JSON_TYPE(json[, '$.path']) | 类型内省(OBJECT/ARRAY/STRING…) |
| JSON_LENGTH | JSON_LENGTH(json[, '$.path']) | 对象键数 / 数组元素数 |
| JSON_KEYS | JSON_KEYS(json[, '$.path']) | 对象键集 |
| JSON_DEPTH | JSON_DEPTH(json) | 嵌套深度 |
| JSON_OBJECT | JSON_OBJECT(k1, v1, …) | 构造 JSON 对象 |
| JSON_ARRAY | JSON_ARRAY(v1, v2, …) | 构造 JSON 数组 |
| JSON_QUOTE | JSON_QUOTE(str) | 字符串转 JSON 字面量 |
| -> (箭头) | json -> '$.path' | 取 JSON 片段(等价 JSON_EXTRACT) |
| ->> (箭头) | json ->> '$.path' | 取标量文本(等价 JSON_VALUE) |
| 函数 | 签名 | 说明 |
|---|---|---|
| SUM | SUM([DISTINCT] x) | 求和 |
| COUNT | COUNT(*) / COUNT([DISTINCT] x) | 计数 |
| AVG | AVG([DISTINCT] x) | 平均值 |
| MIN | MIN(x) | 最小值 |
| MAX | MAX(x) | 最大值 |
| COUNT_IF / COUNTIF | COUNT_IF(cond) | 条件为真的行数 |
| ANY_VALUE | ANY_VALUE(x) | 组内任一值 |
| STDDEV / STDDEV_SAMP | STDDEV(x) | 样本标准差 |
| VARIANCE / VAR_SAMP | VARIANCE(x) | 样本方差 |
| STDDEV_POP | STDDEV_POP(x) | 总体标准差 |
| VAR_POP | VAR_POP(x) | 总体方差 |
| BOOL_AND / EVERY | BOOL_AND(x) | 全真聚合 |
| BOOL_OR | BOOL_OR(x) | 任真聚合 |
| GROUP_CONCAT / LISTAGG / STRING_AGG | GROUP_CONCAT(x[, sep]) | 字符串拼接聚合 |
| PERCENTILE_CONT / MEDIAN | PERCENTILE_CONT(f) WITHIN GROUP (ORDER BY x) | 连续插值分位数(MEDIAN=0.5) |
| PERCENTILE_DISC | PERCENTILE_DISC(f) WITHIN GROUP (ORDER BY x) | 离散分位数 |
| BIT_AND | BIT_AND(x) | 按位与聚合 |
| BIT_OR | BIT_OR(x) | 按位或聚合 |
| BIT_XOR | BIT_XOR(x) | 按位异或聚合 |
| MODE | MODE(x) | 众数(并列取最小) |
| CORR | CORR(y, x) | Pearson 相关系数 |
| COVAR_SAMP / COVAR_POP | COVAR_SAMP(y, x) | 样本 / 总体协方差 |
| REGR_SLOPE / REGR_INTERCEPT / REGR_R2 | REGR_SLOPE(y, x) | 线性回归:斜率 / 截距 / 判定系数 R² |
| REGR_COUNT / REGR_AVGX / REGR_AVGY | REGR_AVGX(y, x) | 回归点数 / 自变量均值 / 因变量均值 |
| REGR_SXX / REGR_SYY / REGR_SXY | REGR_SXY(y, x) | 回归平方和 / 叉积和 |
| APPROX_COUNT_DISTINCT | APPROX_COUNT_DISTINCT(x) | 近似去重计数(亿级基数秒级估算) |
| APPROX_PERCENTILE | APPROX_PERCENTILE(x, p) | 近似分位数(海量数据快速估算) |
| 函数 | 签名(OVER 子句) | 说明 |
|---|---|---|
| ROW_NUMBER | ROW_NUMBER() OVER (…) | 行号(无并列) |
| RANK | RANK() OVER (…) | 排名(并列跳号) |
| DENSE_RANK | DENSE_RANK() OVER (…) | 密集排名(并列不跳号) |
| LAG | LAG(x[, offset[, default]]) OVER (…) | 前 N 行的值 |
| LEAD | LEAD(x[, offset[, default]]) OVER (…) | 后 N 行的值 |
| NTILE | NTILE(n) OVER (…) | 等频分桶 |
| FIRST_VALUE | FIRST_VALUE(x) OVER (…) | 窗口内第一行的值 |
| LAST_VALUE | LAST_VALUE(x) OVER (…) | 窗口内最后一行的值 |
| NTH_VALUE | NTH_VALUE(x, n) OVER (…) | 窗口内第 N 行的值 |
| PERCENT_RANK | PERCENT_RANK() OVER (…) | 百分比排名 |
| CUME_DIST | CUME_DIST() OVER (…) | 累积分布 |
| SUM / AVG / COUNT OVER | SUM(x) OVER (…) | 聚合用作窗口 |
| MIN / MAX OVER | MIN(x) OVER (…) | 聚合用作窗口 |
| PERCENTILE_CONT OVER | PERCENTILE_CONT(f) WITHIN GROUP (ORDER BY x) OVER (…) | 整分区连续分位 |
| PERCENTILE_DISC OVER | PERCENTILE_DISC(f) WITHIN GROUP (ORDER BY x) OVER (…) | 整分区离散分位 |
窗口帧支持 ROWS / RANGE / GROUPS 三种 + 命名窗口(WINDOW w AS …)+ IGNORE NULLS + QUALIFY 过滤窗口结果。
同一个业务需求, 传统数据库 / 大数据引擎写起来又长又绕,YoungsData 一条简洁 SQL 搞定 —— 下面都是真实可运行的写法。
重点导读 · 最建议先看的 4 个硬核例子
如果用户只看这一页的一小部分, 最该点开的就是下面这四段。它们不是“少写几行 SQL”那么简单, 而是直接证明 YoungsData 已经把 Oracle 级行模式识别做成了真正可用的嵌入式能力。
SELECT
JSON_VALUE(payload, '$.user.name') AS user_name,
JSON_VALUE(payload, '$.user.vip') AS vip,
CAST(JSON_VALUE(payload, '$.order.amount') AS DECIMAL(18, 2)) AS amount
FROM events
WHERE JSON_VALUE(payload, '$.status') = 'paid'
AND JSON_VALUE(payload, '$.user.vip') = 'true';
SELECT
payload ->> '$.user.name' AS user_name, -- 按路径直读,免 JSON_VALUE/CAST
payload ->> '$.user.vip' AS vip,
payload ->> '$.order.amount' AS amount
FROM events
WHERE
payload ->> '$.status' = 'paid'
AND payload ->> '$.user.vip' = 'true';
SELECT dept, name, salary
FROM (
SELECT dept, name, salary,
ROW_NUMBER() OVER (PARTITION BY dept ORDER BY salary DESC) AS rn
FROM emp
) t
WHERE rn <= 3;
SELECT
dept, name, salary
FROM
emp
QUALIFY
ROW_NUMBER() OVER (PARTITION BY dept
ORDER BY salary DESC) <= 3;
SELECT product,
SUM(CASE WHEN quarter='Q1' THEN amount END) AS Q1,
SUM(CASE WHEN quarter='Q2' THEN amount END) AS Q2,
SUM(CASE WHEN quarter='Q3' THEN amount END) AS Q3,
SUM(CASE WHEN quarter='Q4' THEN amount END) AS Q4
FROM sales GROUP BY product;
SELECT *
FROM sales
PIVOT (
SUM(amount) FOR quarter IN ('Q1','Q2','Q3','Q4') -- 新增季度只改这一行
);
import org.apache.spark.sql.expressions.Window
import org.apache.spark.sql.functions._
val w = Window.partitionBy("dept")
.orderBy(col("salary").desc)
df.withColumn("rn", row_number().over(w))
.filter(col("rn") <= 3)
.select("dept", "name", "salary")
.show()
SELECT dept, name, salary
FROM emp
QUALIFY ROW_NUMBER() OVER (PARTITION BY dept
ORDER BY salary DESC) <= 3;
// Spark
df.withColumn("day", date_trunc("day", col("ts")))
.groupBy("day", "product")
.agg(sum("amount").alias("revenue"))
.orderBy("day")
.show()
// Flink DataStream 还需 WindowAssigner /
// Watermark / KeyedProcessFunction…
SELECT
DATE_TRUNC('day', ts) AS stat_day, -- 'hour'/'month' 粒度同理
product,
SUM(amount) AS revenue
FROM metrics
GROUP BY
DATE_TRUNC('day', ts),
product
ORDER BY stat_day;
-- 每步先取「最早发生时间」,再层层 JOIN 校验先后
SELECT v.user_id, v.ts AS view_t, c.ts AS cart_t, p.ts AS pay_t
FROM (SELECT user_id, MIN(ts) AS ts
FROM events WHERE event = 'view' GROUP BY user_id) v
JOIN (SELECT user_id, MIN(ts) AS ts
FROM events WHERE event = 'cart' GROUP BY user_id) c
ON c.user_id = v.user_id AND c.ts > v.ts
JOIN (SELECT user_id, MIN(ts) AS ts
FROM events WHERE event = 'pay' GROUP BY user_id) p
ON p.user_id = c.user_id AND p.ts > c.ts;
-- 三层自关联扫三遍表;每加一步漏斗,多一层 JOIN;
-- 且 MIN(ts) 藏语义陷阱:最早加购早于最早浏览时,
-- 真实走完路径的用户会被漏掉
SELECT user_id, view_t, pay_t
FROM events
MATCH_RECOGNIZE (
PARTITION BY user_id -- 每个用户独立匹配
ORDER BY ts -- 事件按时间排成序列
MEASURES FIRST(viewed.ts) AS view_t, -- 取首次浏览时间
LAST(paid.ts) AS pay_t -- 取支付时间
PATTERN (viewed carted+ paid) -- 漏斗:浏览 → 加购(≥1次) → 支付
DEFINE viewed AS event = 'view', -- 每步的判定条件
carted AS event = 'cart',
paid AS event = 'pay'
);
Pattern<Order, ?> p = Pattern
.<Order>begin("down")
.where(new IterativeCondition<Order>() {
public boolean filter(Order o, Context<Order> ctx)
throws Exception {
for (Order prev : ctx.getEventsForPattern("down"))
if (o.amount >= prev.amount) return false;
return true; // 递减才算命中
}
})
.times(3).consecutive();
CEP.pattern(stream.keyBy(o -> o.userId), p)
.select(/* 匹配结果还要自己拆装 */ …);
// 打包 → 提交集群作业;改规则,再来一遍
SELECT
user_id, first_day, last_day
FROM orders
MATCH_RECOGNIZE (
PARTITION BY user_id -- 逐用户检测
ORDER BY order_date -- 按下单时间排序
MEASURES
FIRST(decline.order_date) AS first_day, -- 首笔下跌日
LAST(decline.order_date) AS last_day -- 第三笔下跌日
PATTERN (decline{3}) -- 连续 3 笔递减
DEFINE
decline AS amount < PREV(amount) -- 比上一笔金额小
);
以 100, 90, 80, 70, 60 这组金额为例: 想找出所有“连续 3 笔递减”序列时, 不是只找到 (90,80,70), 还必须继续找到重叠的 (80,70,60)。 传统 SQL / 代码流通常要: 1. 先取第一段 2. 手工回退起点一行 3. 再跑下一段 4. 自己保证边界不重不漏 规则一复杂,实现立刻失控
SELECT
user_id, start_amt, end_amt
FROM orders
MATCH_RECOGNIZE (
PARTITION BY user_id
ORDER BY order_date
MEASURES
FIRST(DOWN.amount) AS start_amt, -- 第 1 笔递减金额
LAST(DOWN.amount) AS end_amt -- 第 3 笔递减金额
AFTER MATCH SKIP TO NEXT ROW -- 命中后从下一行继续,允许重叠
PATTERN (DOWN{3}) -- 连续 3 笔递减
DEFINE
DOWN AS amount < PREV(amount) -- 当前金额小于上一笔
);
真实业务里经常不是“找到了就完了”, 而是还要继续回答: 1. 哪些行是起点? 2. 哪些行是中间过渡? 3. 哪些行只是参与匹配但不应该出现在最终结果里? 很多系统只能告诉你“命中了一个模式”, 但无法把匹配角色和输出裁剪一起做好。
SELECT order_date, amount, cls
FROM orders
MATCH_RECOGNIZE (
PARTITION BY user_id
ORDER BY order_date
MEASURES CLASSIFIER() AS cls -- 返回当前行在模式中的角色名
ALL ROWS PER MATCH -- 逐行输出匹配结果
PATTERN (A {- B+ -} C) -- B 参与匹配,但从最终输出排除
DEFINE A AS amount > 100, -- 起始高值
B AS amount BETWEEN 50 AND 100, -- 中间过渡段
C AS amount < 50 -- 终止低值
);
| 数据库 | MATCH_RECOGNIZE | EXCLUSION {- -} |
|---|---|---|
| Oracle 12c+ | ✓ | ✓ 标准制定者级 |
| Trino / Presto | ✓ | ✓ |
| Snowflake | ✓ | △ 不完整 |
| Flink CEP / 模式能力 | ✓ | △ 部分 |
| PostgreSQL / MySQL / SQL Server | — | — |
| BigQuery / Spark / Databricks | — | — |
| DuckDB / ClickHouse | — | — |
真实业务里要找每一轮“从峰值跌下去, 又涨回峰值”的回撤事件时,用户真正想看的 通常只有两个信号点: 1. 见顶日 2. 修复确认日 中间连续下跌的过程日通常不想报出来, 否则结果会非常脏。 难点在于: 传统库就算能用 window + 自连接硬拼出结果, 也很难做到“把中间行从输出里拿掉, 但回撤幅度 / 下跌天数仍然按这些中间行计算”。
SELECT
sym, episode, role, d, px, trough, down_days, drawdown
FROM quote
MATCH_RECOGNIZE (
PARTITION BY sym
ORDER BY d
MEASURES
MATCH_NUMBER() AS episode, -- 第几轮回撤
CLASSIFIER() AS role, -- 当前是 PEAK 还是 RECOVER
MIN(FALL.px) AS trough, -- 谷底:对隐藏的下跌日统计
COUNT(FALL.px) AS down_days, -- 水下天数:对隐藏行统计
PEAK.px - MIN(FALL.px) AS drawdown -- 最大回撤
ALL ROWS PER MATCH
PATTERN (PEAK {- FALL+ -} RECOVER) -- 中间连跌段参与匹配但不输出
DEFINE
FALL AS px < PREV(px), -- 连续下跌
RECOVER AS px >= PEAK.px -- 涨回峰值即修复
);
真实输出形态
| sym | 第几轮 | 角色 | 日 | 价 | 谷底 | 下跌天数 | 最大回撤 |
|---|---|---|---|---|---|---|---|
| AAPL | 1 | PEAK | 1 | 100 | — | 0 | — |
| AAPL | 1 | RECOVER | 4 | 101 | 85.0 | 2 | 15.0 |
| AAPL | 2 | PEAK | 5 | 120 | — | 0 | — |
| AAPL | 2 | RECOVER | 8 | 122 | 105.0 | 2 | 15.0 |
常见引擎的真实情况通常是: 1. 只支持最浅层的序列匹配,再深一点就没有 2. 没有 CLASSIFIER / MATCH_NUMBER / SUBSET 3. 不支持 AFTER MATCH SKIP TO NEXT ROW 重叠匹配 4. DEFINE 里做变量组聚合或普通函数,很容易直接缺失 5. Oracle 虽然强,但部署形态是重量级服务端,不是单 jar 嵌入式 也就是说: “能写一个漏斗 Demo” 和 “把 Oracle 级高阶语义完整跑通” 中间隔着一整代实现难度
SELECT order_date, amount, match_no, cls, avg_x
FROM orders
MATCH_RECOGNIZE (
PARTITION BY user_id
ORDER BY order_date
MEASURES
MATCH_NUMBER() AS match_no, -- 当前是第几个命中的模式
CLASSIFIER() AS cls, -- 当前行扮演的角色
AVG(X.amount) AS avg_x -- DOWN/NOISE 子集上的运行均值
ALL ROWS PER MATCH
AFTER MATCH SKIP TO NEXT ROW -- 允许重叠匹配
PATTERN (DOWN+ NOISE* RECOVER) -- 下跌序列 + 噪声 + 恢复
SUBSET X = (DOWN, NOISE) -- 定义聚合子集 X
DEFINE
DOWN AS amount < PREV(amount), -- 继续下跌
NOISE AS ABS(amount - PREV(amount)) < 5, -- 小幅波动视为噪声
RECOVER AS amount > AVG(X.amount) -- 高于运行均值才算恢复
);
-- 下面那条 SQL,拿到各引擎试跑:
MySQL : ERROR 1305 — FUNCTION TO_CHAR
does not exist
PostgreSQL : ERROR — function group_concat(text)
does not exist
SQL Server : 'NVL' is not a recognized
built-in function name
Spark : AnalysisException — Undefined
function: GROUP_CONCAT
→ 换引擎 = 上千条存量 SQL 逐条改写、逐条回归,
每一处改写都是一次出错机会
SELECT
TO_CHAR(create_time,'YYYY-MM') AS stat_month, -- Oracle 写法
GROUP_CONCAT(DISTINCT region) AS regions, -- MySQL 写法
NVL(SUM(amount), 0) AS revenue -- Oracle 写法
FROM orders
GROUP BY TO_CHAR(create_time,'YYYY-MM');
-- 一条 SQL 混写两家方言,YoungsData 直接执行 ✓
一页速查:支持哪些 SQL 语法、开放哪些编程接口。函数明细见 §14 函数手册。
| 类别 | 支持 |
|---|---|
| 语句 | SELECT · INSERT(含 INSERT ... SELECT)· UPDATE · DELETE · CREATE / ALTER / DROP / TRUNCATE(DDL)· CREATE TABLE ... AS SELECT(按查询输出类型建表);UPDATE / DELETE 的 WHERE 可含子查询(高风险形态默认关, 显式开启);DELETE ... LIMIT n 有界删除;DELETE ... RETURNING * 删除并返回被删行(独立服务形态) |
| 元数据内省 | DESCRIBE · DESC · SHOW TABLES[LIKE] · SHOW COLUMNS FROM · SHOW INDEXES / INDEX / KEYS FROM · SHOW CREATE TABLE · SHOW SHARDS FROM · SHOW HISTORY FOR;返回普通结果集, 可被程序直接消费 |
| SELECT 子句 | WHERE · GROUP BY · HAVING · DISTINCT · ORDER BY(ASC/DESC · NULLS FIRST/LAST)· LIMIT · OFFSET · FETCH · QUALIFY · WITH RESUME / RESUME ?(断点续读, 独立服务形态) |
| JOIN | INNER · LEFT · RIGHT · FULL · CROSS · NATURAL · 逗号隐式;ON / USING;自关联 · 派生表 / 子查询作一侧 |
| 集合运算 | UNION · UNION ALL · INTERSECT · EXCEPT |
| 子查询 | 标量 · IN / NOT IN · EXISTS · ANY / SOME · ALL · 行构造 (a,b) IN · 关联去关联 · 派生表 · LATERAL |
| CTE | WITH(非递归链式)· WITH RECURSIVE(递归到不动点) |
| 窗口 & 帧 | 18 窗口函数;PARTITION BY / ORDER BY;帧 ROWS / RANGE / GROUPS + 五种边界;EXCLUDE 四态;IGNORE / RESPECT NULLS |
| 多维聚合 | GROUP BY · ROLLUP · CUBE · GROUPING SETS · GROUPING() |
| 比较运算 | = · <> · > · >= · < · <= · IN · LIKE / ILIKE · REGEXP / RLIKE · SIMILAR TO · BETWEEN · IS [NOT] NULL · IS [NOT] DISTINCT FROM · <=>;时间列可直接与字符串字面量 / 参数比较(WHERE d >= '2026-01-01', 与写 DATE '…' 结果一致;格式不认当场报错) |
| 逻辑 / 算术 | AND · OR · NOT;+ - * / %;CONCAT · || ;JSON -> · ->> ;位运算函数 |
| 高级表算子 | 派生表 · VALUES · TABLESAMPLE · PIVOT · UNPIVOT · MATCH_RECOGNIZE · LATERAL |
| 表达式 | CASE WHEN(简单 / 搜索)· INTERVAL 运算 · CAST / TRY_CAST / SAFE_CAST · 参数 ? · AS 别名 |
| 数据类型 | INT · BIGINT · FLOAT · DOUBLE · DECIMAL · VARCHAR · CHAR · BOOLEAN · DATE · TIME · TIMESTAMP;时间为墙钟语义——存进去的年月日时分秒读出来一字不差, 与服务器时区 / 会话时区无关 |
| 函数 | 标量约 182 · 聚合 38(含统计 / 回归 / 分位 / 近似)· 窗口 18 —— 明细见 §14 函数手册 |
| 方言 | MySQL(默认)· PostgreSQL · Oracle —— 无冲突并包, 仅冲突处按方言 |
| 入口 | 职责 | 关键方法 |
|---|---|---|
| SqlEngine | 装配入口 | builder(dir).build() · sql() · db() · close() |
| SqlEngine.Builder | 链式装配 | transactional · snapshots · pkIndexed · declarativeIndexes · sharded · historyByDay · dialect · readOnly · noDdl |
| SqlQuery | SQL 文本门面 | list · forEach · cursor · first · one · exists · count · executeInsert/Update/Delete · executeDdl · execute · prepare* · begin · transact · row() |
| PreparedSqlQuery | 预编译 SELECT | list(args) · page · forEach · first · one · exists · count · cursor(args) · row() |
| Prepared{Insert/Update/Delete} | 预编译 DML | execute(args) · executeBatch(List) |
| Db | Fluent no-SQL 门面 | select · insertInto · update · deleteFrom · createTable · createIndex · fromSpec |
| Db.Select | 查询构造器 | from · join / leftJoin / … · where · groupBy · having · distinct · orderBy · limit / offset · union / … · list / cursor / one / count |
| D | 表达式 / 条件工厂 | col · val · param · fn · cast · caseWhen · win · 聚合 sum / avg / … · 比较 eq / gt / in / … · 逻辑 and / or / not · 窗口 rowNumber / lag / … |
| SqlTxn | 事务句柄 | executeInsert/Update/Delete · list / one / … · commit · commitAsync · rollback · db() |
| Cursor | 拉取式游标 | next() · columnNames() · close() |
| SessionContext | 会话参数(不可变) | defaults() · withDialect · withCastMode · withAuditSingleScan · withScanObserver · withSpillBudgetBytes · withZoneId |
| spec(QuerySpec / SpecCodec) | RPC 电报(wire) | QuerySpec.select(…) · SpecCodec.encode / decode · Db.fromSpec(byte[]) |
└─ 三种写法(SQL 文本 SqlQuery · Fluent Db+D · RPC 电报 spec)语义一致、可混用;详见 §07 编程式 API。
一句话总结
YoungsData 有 Oracle / Trino 级别的高阶分析语法, 有 DuckDB 级别的嵌入式轻量, 有 大数据引擎级别的永不 OOM 鲁棒性, 还有任何主流数据库都不具备的跨方言统一能力与高效 JSON 处理。
尤其在 Oracle 级 MATCH_RECOGNIZE 高阶语义这件事上, 我们不是"能演示", 而是已经把整套高级语义做成了真正可用的产品能力 —— 在国产 SQL 数据引擎这条赛道上, 这是极少数团队能做到的深度。