历史上,动态报告的任务并未考虑可变数量的分组,标准SQL是基于静态描述的查询,并具有明确的结果模式。随着BI平台和交互式仪表板的出现,程序员开始寻找方法以“实时”构造SQL查询,以便根据用户请求生成复杂的多重数据切片。
问题 — 在SELECT内部不能使用普通的if/else逻辑编写SQL — 列的数量、字段,甚至GROUP BY在查询编译阶段就已经确定。如果用户希望按多个任意字段进行切片,则必须动态构建查询文本并通过EXECUTE/Dynamic SQL执行。
解决方案:
在外部应用中生成SQL代码,然后通过EXEC/EXECUTE调用。在某些情况下,对于简单的任务,会使用CASE和硬编码模板,但为了灵活性,始终会诉诸动态SQL:
示例代码(伪代码):
-- 在应用程序中(例如,Python) groups = ['region', 'channel', 'month'] columns = [f'SUM({col}) AS {col}_sum' for col in selected_metrics] group_by = ', '.join(groups) selects = ', '.join(groups + columns) query = f'SELECT {selects} FROM sales GROUP BY {group_by}' -- 然后通过应用程序/SQL接口发送此查询
在支持EXECUTE的数据库中:
DECLARE @sql NVARCHAR(MAX); SET @sql = N'SELECT ' + @selects + ' FROM sales GROUP BY ' + @group_by + ';'; EXEC sp_executesql @sql;
关键特性:
是否可以仅通过标准SELECT实现动态列数的变化(pivot/unpivot),而无需动态SQL?
不可以 — 只有事先已知的值可以通过CASE/DECODE“展开”,但未知列数只能通过动态方式实现。
如果使用字符串拼接,您动态SQL的逻辑是否确保防止注入?
不,手动字符串拼接始终存在SQL注入风险。务必对字段/分组列表进行白名单验证,避免未检查的用户片段,最好在可能的情况下使用参数。
GROUP BY可以通过变量接收列列表吗?
标准SQL不支持通过变量/参数传递列列表到GROUP BY。必须动态形成查询文本 — 仅仅在GROUP BY列表中使用变量是不行的。
BI工程师允许用户直接通过应用程序接口传递报告字段名称 — 而不进行白名单过滤。结果在注入测试中,因字段名中的恶意代码而“撞毁”了生产表。
优点:
缺点:
工程师实施严格的名称验证 — 用户只能从允许的列(来自config/metadata)中选择,动态SQL仅根据白名单构建,没有注入的途径。
优点:
缺点: