用一段sql作为指标计算的模板
在实际的项目中,往往有些指标已经有写好的大段逻辑复杂的sql了。在这之中,只要做到类似于填槽的工作即可。本文档来介绍一下如何实现sql代码填槽功能。
假设现在有一张表,schema如下:

现在我们要实现一个叫test的指标,他最终的sql版本计算公式为:
SELECT
sum(case when dim1 = '${问答中dim1的值}' then qty else 0 end) /
sum(case when dim2 = '${问答中dim2的值}' then qty else 0 end) AS r
FROM e1
${问答中其他筛选条件,例如时间条件}
这种情况下,代码配置如下(具体解释见备注):
async (lf, _self, config, { execute, commonLib, moment, DBConnector }) => {
// 问答中dim1的值和问答中dim2的值先从logicform中拿出来
const dim1Query = lf.query.维度1;
const dim2Query = lf.query.维度2;
// 可以根据业务需求适当报错
if(!dim1Query) throw new Error('此指标必须要带上维度1');
if(!dim2Query) throw new Error('此指标必须要带上维度2');
// 保留全局筛选条件(例如时间)
const globalQuery = JSON.parse(JSON.stringify(lf.query));
delete globalQuery.维度1;
delete globalQuery.维度2;
// 用globalQuery替换老的lf。输出模式选择sqlComponents获取其中的sql。可以把Sql中的全局where条件拿到。
const sqlComponents = await execute({
...lf,
preds: [{"operator":"$sum", "pred": "销量"}], // 一定要覆盖掉preds,不然会无限循环调用,可以随便搞一个
query: globalQuery,
output: {
type: 'sqlComponents' // 这里是核心,让execute函数不执行实际的sql,而是单纯地返回sql的各个部分。
}
})
// 打印调试,可以在执行系统设置 -> 日志与记录 -> 后台日志 处查看
console.log('DEBUG sqlComponents: ', JSON.stringify(sqlComponents.result[0]));
// 根据变量生成最终的SQL
const sqlTemplate = `
SELECT
sum(case when dim1 = '${dim1Query}' then qty else 0 end) /
sum(case when dim2 = '${dim2Query}' then qty else 0 end) AS r
FROM e1
${sqlComponents.result[0].where}
`
console.log('DEBUG sqlTemplate: ', sqlTemplate);
// 把生成的SQL放在的sqls记录中,用于前端Debug
config.sqls.push(sqlTemplate);
// 跑一下SQL
const result = await DBConnector.runSQL(sqlTemplate);
console.log('DEBUG result: ', result);
// 把sql的结果,按照我们的需求的结构返回出来。
return [{_id: "0", [lf.preds[0].name]: result[0].r}]
}
最后问答效果:

最后生成的sql
SELECT sum(case when dim1 = 'd1' then qty
else 0
end) / sum(case when dim2 = 'd2' then qty
else 0
end) AS r
FROM e1
WHERE `e1`.`time` >= '2025-01-01 00:00:00'
AND `e1`.`time` <= '2025-03-12 23:59:59';
分组怎么办
如果客户问到带分组的问题。sql实现还要考虑groupby,可能过于复杂。这个时候,可以在shouldSplitGroupby函数中配置:
() => false