文档首页 / SQL 参数与动态语法

SQL 参数与动态语法

SQL 数据集中 $参数、${表达式}、#{脚本}、整条 SQL 脚本、集合参数、注释与分号处理、注入校验规则。

概述

SQL 数据集的 SQL 文本在执行前会经过模板处理。共有四种写法,遵循一条原则:值走 ${},结构走 #{}。

写法 处理结果 用途 示例
$参数名 绑定为预编译占位符 ? 值 WHERE id = $userId
${表达式} 表达式求值后绑定为 ? 值 WHERE d >= ${addDays(now(), -30)}
#{脚本} 结果直接拼进 SQL 文本 结构(子句、表名、排序) #{isEmpty($s) ? '' : 'AND status = $s'}
整条 SQL 被 ${...} 包裹 脚本生成整段 SQL 结构 见下文

$参数名 等价于 ${$参数名},单个参数直接写前者即可;只有需要拼接、函数或四则运算时才写 ${...}。

功能入口

在 SQL 数据集、公共数据集与数据大屏的 SQL 编辑器中书写。编辑器右侧「语法」「函数」「代码片段」标签页有速查。

操作步骤

用 $参数名 传值

SELECT visit_id, dept_code, fee
FROM outpatient_visit
WHERE dept_code = $deptCode
  AND visit_date >= $startDate

$ 后的名字须是已声明的报表参数。${库房} 这种没有内层 $ 的写法不是参数引用:库房 是未定义标识符,求值为空(null),绑定为 NULL;取参数值须写 $库房 或 ${$库房}。裸写的 $名 不会在引号内、${}/{} 块内、或 $$ 内置变量处被识别。

用 ${表达式} 计算值

表达式可调用内置函数,结果作为一个绑定值:

WHERE visit_date >= ${addDays(now(), -$days)}
  AND visit_date <  ${now()}

${} 正文遇到第一个 } 即结束,所以正文里不能出现 }。函数清单见 内置函数。

LIKE 模糊查询

占位符不能写在引号里,'${$kw}' 会被判为定义错误。正确写法是把通配符拼进表达式:

WHERE patient_name LIKE ${'%' + $kw + '%'}

用 #{} 拼结构

#{} 的结果原样进入 SQL,正文按花括号配平,可写块和映射字面量。正文只有一个表达式时自动返回;含 var、if/else 等多条语句时必须显式 return,否则拼出空串。

可选条件(值仍写成 $名,拼入后再被绑定):

SELECT visit_id, dept_code, fee
FROM outpatient_visit
WHERE 1 = 1
  #{isEmpty($deptCode) ? '' : 'AND dept_code = $deptCode'}
  #{$minFee != null ? 'AND fee >= $minFee' : ''}

带白名单的动态排序(多语句,必须 return):

SELECT visit_id, dept_code, fee, visit_date
FROM outpatient_visit
ORDER BY #{
    var allowed = ['fee', 'visit_date', 'dept_code'];
    return allowed.contains($sortField) ? $sortField : 'visit_date'
} #{$sortDir == 'ASC' ? 'ASC' : 'DESC'}

表名、列名只能用 #{} 拼入,必须做白名单,不要直接拼接参数。

整条 SQL 由脚本生成

整条 SQL 被 ${...} 包裹时,内部作为脚本执行,返回值就是最终 SQL;返回文本里的 #{}、${} 会再被处理一次:

${
    if ($reportType == 'detail') {
        return "SELECT visit_id, dept_code, fee FROM outpatient_visit ORDER BY visit_date DESC"
    } else {
        return "SELECT dept_code, SUM(fee) AS total_fee FROM outpatient_visit GROUP BY dept_code"
    }
}

安全限制:结果里出现的 ${、#{ 必须原样出现在脚本源码里,否则被拒绝,防止参数值携带模板片段。

集合参数

把报表参数类型设为「集合」,用 IN (${$名}):

WHERE dept_code IN (${$deptCodes})

引擎按元素个数把一个 ? 展开为 ?,?,?。参数值可以是多选控件传入的集合,也可以是字符串 1,2,3、全角逗号或顿号分隔的字符串、JSON 数组字符串。空集合绑成 IN (NULL),结果为空而不报错。

「没选就不过滤」:

WHERE 1 = 1
  #{isEmpty($deptCodes) ? '' : 'AND dept_code IN (${$deptCodes})'}

绑定时日期带时间的用 setTimestamp,仅日期的用 setDate。不要用 #{$ids} 拼集合:集合被转成 [1, 2, 3],产生 IN ([1, 2, 3]) 语法错误,空集合则是 IN ()。

系统参数与当前用户

写法 含义
${$$config.名称} 「系统 > 自定义参数」中的配置值,SQL 里必须写成 ${$$config.名称},裸写 $$config.名称 不会被转换
${userAccount()} 当前登录账号,仅在有登录上下文时有值
${username()} 当前登录用户姓名,仅在有登录上下文时有值

行级权限示例:

SELECT visit_id, patient_name, fee
FROM outpatient_visit
WHERE doctor_account = ${userAccount()}

属性说明

处理顺序

步骤 处理
1 整条 SQL 是 ${...} 时先执行脚本
2 展开 #{}
3 去除注释:-- 行注释、/* */ 块注释
4 裸 $变量 转为 ${$变量}
5 提取 ${}
6 ${} 替换为 ? 并记录绑定值
7 识别存储过程的输出参数声明
8 去除末尾分号

同一条 SQL 中相同的表达式只求值一次。

尾部分号

末尾的 ;(含前后空白、连续多个)会被自动去除,字符串字面量中和语句中间的分号不动。语句中间的分号会被注入校验拒绝。

注入校验规则

对去掉字符串字面量后的 SQL 结构做校验,拒绝下列内容:

  • 整词 drop、delete、insert、update、create、alter、truncate、exec、execute、script、declare。
  • 十六进制字面量 0x…,xp_ 扩展存储过程,以及 sp_executesql、sp_cmdshell 等系统过程(自建的 sp_xxx 过程允许)。
  • 语句中间的 ;,OR x=x 形式,非开头位置的 UNION SELECT,单引号个数为奇数。

字段名 create_time、update_time 与 WHERE 1=1 不受影响。

注意:${} 只会绑定为预编译参数 ?,不能生成 AND、IN、ORDER BY 等 SQL 结构。表达式的值看起来是 SQL 片段时,执行报错「SQL 中第N个 ${...} 表达式看起来在生成动态 SQL 片段……请改用 #{}」。

示例

综合示例:按时间范围、可选科室、动态排序查询门诊费用。报表参数:startDate、endDate(日期),deptCodes(集合,可空),sortField、sortDir(字符串)。

SELECT visit_id, dept_code, fee, visit_date
FROM outpatient_visit
WHERE visit_date >= $startDate
  AND visit_date <  $endDate
  #{isEmpty($deptCodes) ? '' : 'AND dept_code IN (${$deptCodes})'}
ORDER BY #{
    var allowed = ['fee', 'visit_date', 'dept_code'];
    return allowed.contains($sortField) ? $sortField : 'visit_date'
} #{$sortDir == 'ASC' ? 'ASC' : 'DESC'}

注意事项

注意:不要把值拼成字面量,例如 'AND dept_code = ' + "'" + $deptCode + "'"。这样值进入了 SQL 文本,有注入风险,也可能被单引号配对校验拦下。

注意:数值参数不要用 isEmpty 判空,改用 $x != null。

提示:对列做函数运算(如 YEAR(visit_date) = $year)会让索引失效,数据量大时改为先算好边界再比较:visit_date >= ${...}。

相关文档

联系我们

请填写您的信息,我们将在 1 个工作日内与您联系。