同一份巡检 SQL,测试环境查 app_test 模式,生产环境查 app_core 模式。复制两份文件当然能跑,但时间一长,两份脚本一定会慢慢长得不一样。
Ksql 变量可以减少这种重复。不过变量替换很直接,写得随意也会把外部文本原样拼进 SQL。省事和安全之间,要先分清边界。

--- 定义变量不难,难的是判断它代表普通值还是对象名称。
先看一个简单例子
在 Ksql 中定义变量:
text
\set begin_date '2026-08-01'
用于 SQL 文字值时,使用带引号保护的形式:
sql
SELECT order_id, created_at
FROM app_core.orders
WHERE created_at >= :'begin_date';
:'begin_date' 表示把变量作为 SQL 文字处理。它和直接写 :begin_date 不一样;后者属于原样替换,内容如果带有引号或其他片段,可能改变整条 SQL 的结构。
官方手册明确提醒,未经引用的变量值会按字面复制,必须确认放置位置安全。这个提醒不能略过。
对象名和普通值不是一回事
模式名、表名和列名属于标识符,日期、状态、编号属于普通值。两类内容需要不同的引用方式。
例如把模式名设成变量:
text
\set target_schema 'app_core'
引用标识符时可以使用对应的标识符引用形式:
sql
SELECT COUNT(*)
FROM :"target_schema".orders;
别为了少写一点,把完整的 app_core.orders WHERE ... 都塞进一个变量再原样展开。变量范围越大,越难审核,也越容易把不该出现的 SQL 片段带进去。
外部输入不能直接相信
如果变量值来自人工输入、文件、流水线参数或接口,就按不可信输入处理。日期要校验格式,环境名要使用白名单,对象名要限制在审核过的集合里。
比如环境只允许 test 和 prod,外层 PowerShell 应先判断,不在集合中就退出,而不是把任何字符串直接传给 Ksql。
powershell
$kesArticleEnv = 'test'
if ($kesArticleEnv -notin @('test', 'prod')) {
throw 'Invalid environment name.'
}
这里的变量名故意写得明确,避免和系统环境变量混在一起。生产自动化还应由凭据系统、任务平台和权限控制共同兜底,Ksql 变量不是安全沙箱。
启动脚本时传变量
批处理可以在调用 Ksql 时通过参数预先设置变量,再执行同一份 SQL 文件。具体选项以当前 Ksql 的 --help 和版本手册为准。
无论用哪种传法,脚本开头都应该打印非敏感的运行上下文:目标环境、数据库、模式、业务日期和脚本版本。密码、令牌和私钥不能打印。
我还会在真正执行前做一次只读预检,确认变量展开后命中了预期对象和数据范围。涉及写操作时,不要把完整变更 SQL 直接回显到可能收集敏感值的公共日志。
变量不是服务端参数化查询
这点很容易混淆。Ksql 变量主要在客户端做文本替换,方便脚本复用;应用程序里的预编译、绑定参数属于另一层机制。
所以不能因为写了 :name 就认为已经天然防住所有注入。引用形式、输入校验、权限最小化仍然要做。长期运行的业务应用,也不该用 Ksql 文本替换代替驱动提供的参数绑定。
怎么验证展开没有跑偏
先用无副作用查询验证:
sql
SELECT :'begin_date' AS chosen_begin_date,
current_database() AS db_name,
current_user AS login_user;
确认输出与任务单一致,再执行目标查询。对象变量则先用 Ksql 元命令或数据字典确认对象存在、所有者正确,不要用一次大范围业务查询来试错。
安全使用的成功标志

--- 能复用只是及格,输入受控、展开可核对才算稳。
交付前确认普通值和标识符使用了各自的引用方式;外部输入经过白名单或格式校验;日志只打印非敏感上下文;脚本在变量缺失、值非法或目标不符时会停止;最终结果范围与人工预期一致。
Ksql 变量适合把巡检和维护脚本做成一份可配置模板。它很好用,但别神化。把它当成受控文本替换,就会自然记得校验、引用和验收,而不是把安全责任全推给一个冒号。