带有动态过滤、分组和多个参数聚合的分析报告是 BI 和数据仓库应用程序的核心。以前,这类任务只能通过静态查询来解决,但随着需求的增长,出现了动态构建 SQL 的需求:字段和分组的数量由用户或应用程序决定。
问题 — 不能提前确定所有分组和聚合字段的变体。必须动态构建查询,这可能导致错误和在请求计划不佳和使用不当索引时的性能下降。
解决方案 — 通过动态 SQL(例如,通过 T-SQL 中的 EXEC 或 sp_executesql)实现查询构建,从用户参数中生成 SELECT、GROUP BY 和相应聚合函数的字段列表。同时,必须仔细转义字段名称并确保防止 SQL 注入。以下是 SQL Server 的示例:
DECLARE @select_fields nvarchar(max) = N'customer_id, year'; DECLARE @agg_fields nvarchar(max) = N'SUM(amount) AS total, COUNT(*) AS row_count'; DECLARE @group_by nvarchar(max) = N'customer_id, year'; DECLARE @sql nvarchar(max) = N'SELECT ' + @select_fields + ', ' + @agg_fields + N' FROM sales WHERE sale_date >= @start AND sale_date <= @end GROUP BY ' + @group_by; EXEC sp_executesql @sql, N'@start DATE, @end DATE', @start='2023-01-01', @end='2023-12-31';
关键特点:
是否可以提前创建一个通用查询,使用 CASE 处理所有可能的分组,而不是动态 SQL?
不可以,CASE 允许在 SELECT 中进行计算,但无法根据动态确定的字段形成 GROUP BY — 仍然需要显式指定。静态方法对于实时 BI 分析太过限制。
在动态构建 WHERE 时,是否需要使用 SQL 查询参数,还是可以直接将值插入字符串文本中?
始终使用参数(例如,通过 sp_executesql),否则会导致 SQL 注入漏洞,且受保护的值(例如字符串中的撇号)会被错误处理。
创建针对所有可能字段组合的索引是否会加快任何报告的速度?
不,创建多个字段的组合索引会导致索引大小增长和插入/更新速度下降,而只有严格有限的场景会加速。仅对实际需要的字段/组进行索引。
在旧的 BI 报告中,用 if-else "硬编码" 了 30 种分组选项。每添加一个新参数就需要更新源代码和索引架构。 优点:
缺点:
使用动态 SQL 动态形成 select/group by,SQL 代码放在单独模块中,参数严格进行转义。添加新字段 — 只是配置字典,索引根据过滤频率添加。 优点:
缺点: