install
source · Clone the upstream repo
git clone https://github.com/openclaw/skills
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/23396599/report-sql" ~/.claude/skills/openclaw-skills-report-sql && rm -rf "$T"
OpenClaw · Install into ~/.openclaw/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/openclaw/skills "$T" && mkdir -p ~/.openclaw/skills && cp -r "$T/skills/23396599/report-sql" ~/.openclaw/skills/openclaw-skills-report-sql && rm -rf "$T"
manifest:
skills/23396599/report-sql/SKILL.mdsource content
report-sql
本技能由老板亲授,定义 report-service 的 SQL 模板协议与变量规范。
Variables
report-service 变量遵循唯一、精确、零容错原则:
✅ 合法语法
#@option.where.<field>#
🔑 当前支持字段
| 字段名 | 类型 | 示例值 | 典型 SQL 用法 |
|---|---|---|---|
| string | | |
| array | | |
| date | | |
| number | | |
⚠️ 严禁写法(运行时直接失败)
,#@name#
,#name#
→ 无命名空间;@name@
(缺#@option.name#
)→ 路径错误;.where
,s.name
→ 别名+字段,非变量;b.status- 变量内含空格、换行、注释 → 破坏字面匹配。
📌 拼接原则
变量只提供值,表别名由
FROM 子句决定:
- 若
→ 写FROM sale s
;s.name = #@option.where.name# - 若
→ 写FROM blog b
;b.title LIKE CONCAT('%', #@option.where.name#, '%') - 变量本身永远不带
或s.
前缀。b.
✅ 新增:${...}
块级变量语法(2026-03-23 补充)
${...}用于包裹条件片段,典型形式:
${ AND `corpId` = '#@option.where.corpId#' }
${ AND `creatorId` = '#@option.where.creatorId#' }
规则说明
是完整 SQL 片段容器,内部可含任意合法 SQL(含换行、缩进、注释);${...}
内部仍使用${...}
注入具体值;#@option.where.xxx#
块整体按需渲染:若块内所有${...}
均有值,则整个块保留;若任一#@...#
为空/未传,则整块被剔除(空安全);#@...#- 不支持嵌套
;${...}
外不可加额外空格或符号(如${...}
或${...}
——AND ${...}
必须写在块内)。AND
✅ 新增:模糊匹配常用块(2026-03-23 补充)
用于
LIKE 模糊查询,典型形式:
${ AND `name` LIKE '%#@option.where.name#%' }
使用说明
包裹确保前后通配;%...%- 该块同样遵循
空安全规则:若${...}
未传值,整块自动剔除;#@option.where.name# - 如需左匹配(
)或右匹配(xxx%
),请显式写出对应%xxx
符号。%
✅ 新增:时间范围查询常用块(2026-03-23 补充)
用于
createdAt 等时间字段的开闭区间查询,典型形式:
${ AND `createdAt` >= '#@option.where.createdAt[0]#' AND `createdAt` < '#@option.where.createdAt[1]#' }
使用说明
/[0]
表示数组索引,要求传入长度为 2 的时间字符串数组(如[1]
);["2026-03-01 00:00:00", "2026-03-02 00:00:00"]- 语义为
,实现标准左闭右开区间;>= start AND < end - 该块同样遵循
空安全:任一索引值缺失 → 整块剔除。${...}
✅ 新增:多值循环块语法(2026-03-23 补充)
用于生成
IN 或多条件 OR 逻辑,典型形式:
$@option.where.state -> OR { AND [state = '#@item#'] }
使用说明
声明待遍历的数组变量(如$@option.where.state ->
);['draft','published']
在循环中自动替换为当前数组元素;#@item#
是协议级语法糖:引擎会将每个OR { ... }
块内容作为原子条件,用{ ... }
连接,并自动包裹外层括号OR
,最终与块外固定条件组合(如(... OR ...)
),确保 SQL 语义清晰、结构合法;AND (...)- 此机制保证生成的 SQL 永远合法(避免
/AND
优先级错误);OR - 该语法天然防 SQL 注入(值经模板引擎安全转义);
- 若
为空数组或未传,整段被剔除。@option.where.state
🔁 进阶说明:括号语义与等价性
$@... -> OR { ... } 生成的 SQL 自动为每个 { ... } 块添加括号,例如:
$@option.where.types -> OR { AND state = 'finished' AND [type = '#@item#'] }
解析为:
( state = 'finished' AND type = 'sale' ) OR ( state = 'finished' AND type = 'refund' )
此形式在逻辑上 完全等价于:
state = 'finished' AND type IN ('sale', 'refund')
或:
AND state = 'finished' AND ( (type = 'sale') OR (type = 'refund') )
✅ 优势:括号强制分组,杜绝
AND/OR 优先级陷阱;
✅ 优势:可轻松扩展为多字段组合(如 AND [type = '#@item#'] AND [status = '#@item2#']);
⚠️ 注意:若需外层统一 AND(如所有 OR 结果再 AND 一个固定条件),请将该固定条件写在 OR { ... } 块之外(如 WHERE active = 1 AND (...)),而非塞进块内。
🌐 高阶用法:对象数组遍历(2026-03-23 补充)
$@... -> OR { ... } 支持遍历对象数组,并用点语法 #@item.xxx# 访问嵌套字段。
典型形式:
$@searchOrder -> OR { AND [id = '#@item.corpId#'] }
解析逻辑:
- 若
,searchOrder = [{"id":1,"corpId":"wx123"},{"id":2,"corpId":"wx456"}] - 则
分别取值#@item.corpId#
和"wx123"
,"wx456" - 最终生成:
( id = 'wx123' ) OR ( id = 'wx456' )
✅ 优势:直接复用上一步查询结果,实现“查完即用”的流水线; ⚠️ 注意:
#@item.xxx# 中的 xxx 必须是对象存在的字段名,否则该元素被跳过(空安全)。
✅ 新增:复杂 EXISTS 子查询块(2026-03-23 补充)
用于封装高复用业务逻辑(如分发状态判定),典型形式:
${ AND NOT ( EXISTS( SELECT #@option.where.distributeYes# FROM wq.`joblist` j WHERE j.`JobId` = b.id AND j.active = 1 AND (j.`outUserID` = '' OR j.outUserID IS NULL)) OR (NOT EXISTS(SELECT 1 FROM wq.`joblist` j WHERE j.`JobId` = b.id AND j.active = 1 AND (j.`outUserID` = '' OR j.outUserID IS NULL)) and (b.outUserID is null or b.outUserID = '')))}
${ AND (EXISTS(SELECT #@option.where.distributeNo# FROM wq.`joblist` j WHERE j.`JobId` = b.id AND j.active = 1 AND (j.`outUserID` = '' OR j.outUserID IS NULL)) OR (NOT EXISTS(SELECT 1 FROM wq.`joblist` j WHERE j.`JobId` = b.id AND j.active = 1 AND (j.`outUserID` = '' OR j.outUserID IS NULL)) and (b.outUserID is null or b.outUserID = '')))}
使用说明
- 此类块将复杂 EXISTS 逻辑固化为可插拔条件单元,模板中只需声明变量,无需重复书写长 SQL;
/#@option.where.distributeYes#
仍为原子值注入(如#@option.where.distributeNo#
或1
),子查询结构由块体保证;'true'- 同样遵循
空安全:任一${...}
缺失 → 整块剔除;#@...# - 表别名(
/j.
)由块内 SQL 自行定义,与外部b.
无关。FROM
✅ 新增:变量使用场景 — 块级边界原则(2026-03-23 补充)
${...} 块不是“值占位符”,而是自洽 SQL 片段容器。其边界必须包裹完整语法单元。
❌ 危险写法(绝对禁止)
-- 错!and 在块外 → 空时生成语法错误:`WHERE active = 1 and ` select id, parent, name from department where active = 1 and ${ corpId = '#@option.where.corpId#' }
✅ 正确写法(必须)
-- 对!and 在块内 → 空时整块剔除,SQL 依然合法 select id, parent, name from department where active = 1 ${ and corpId = '#@option.where.corpId#' }
📌 核心原则
- 所有逻辑词(
/AND
/OR
/IN
等)必须写在BETWEEN
块内;${...} - 块内 SQL 必须是独立、可执行的语法片段(能单独
);EXPLAIN - 块外只允许固定 SQL(如
),绝不拼接动态逻辑。SELECT ... FROM ... WHERE
这是
空安全的最后防线 —— 宁可多写一个report-service,不可少包一行 SQL。and
Transform
report-service 支持链式数据变换操作,用于处理 SQL 查询结果,支持初始化、条件赋值、字段映射、关联补全与结果组装。
变换语法说明
所有变换以 JSON 数组形式声明,每个对象为一个操作步骤,按顺序执行。
| 字段 | 类型 | 说明 |
|---|---|---|
| string | 操作类型( / / ) |
| string | 目标变量名(如 , ),以 开头 |
| any | 源数据(可为字面值、数组、对象、或另一变量如 ) |
| string | 执行前置条件(如 ,当该变量有值时才执行此步) |
/ | array | 字段映射规则( → ,可设 ) |
/ | string | 关联键名(用于 ) |
示例 1:结果初始化与条件赋值
[ { "method": "setData", "target": "@result", "source": [] }, { "method": "setData", "target": "@result", "source": "@deptList", "precondition": "#@deptList#" } ]
- 作用:先初始化
为空数组;若@result
存在,则用其覆盖@deptList
。@result - 关键:
实现空安全分支。precondition
💡 权威说明(老板亲授):
的语义是——判断上一步产生的中间变量precondition: "#@deptList#"是否存在且非空(非@deptList/null/undefined/[]); 仅当该条件为真时,第二步{}才执行; 第一步setData确保source: []始终有默认值,避免未定义状态。@result
示例 2:对象字段提取与结构重组
[ { "method": "setData", "target": "@result", "source": {} }, { "method": "copyObject", "target": "@result", "source": "@countOrder[0]", "attributes": [ { "from": "totalCount", "to": "count", "default": 0 }, { "from": "totalMoney", "to": "money", "default": 0 } ] } ]
- 作用:先初始化
为空对象@result
;再从{}
(数组首项,必须是对象)中提取@countOrder[0]
→totalCount
、count
→totalMoney
,缺失时用money
填充。default - 关键:
支持字段级映射、类型安全兜底,且目标必须是对象(非数组)。copyObject
示例 3:多源关联与标准响应组装
[ { "method": "setData", "target": "@rows", "source": [] }, { "method": "setData", "target": "@rows", "source": "@searchOrder", "precondition": "#@searchOrder#" }, { "method": "leftJoin", "target": "@rows", "source": "@searchCorp", "targetKey": "corpId", "sourceKey": "id", "fields": [ { "from": "name", "to": "corpName", "default": "" }, { "from": "nickname", "to": "corpNickname", "default": "" } ] }, { "method": "copyObject", "target": "@result", "source": "@rows", "to": "rows", "default": [] } ]
- 作用:
- 初始化
为空数组;@rows - 若
存在,则赋值;@searchOrder - 用
左关联@searchCorp
(@rows
↔corpId
),补全id
/corpName
;corpNickname - 将最终
数组整体写入@rows
(非覆盖@result.rows
)。@result
- 初始化
- 关键:
保障跨源数据一致性,leftJoin
的copyObject
实现标准响应结构(to: "rows"
)。{ "rows": [...] }