在 ERP、BI 等系统中,数据透视分析(Pivot Analysis)是非常常见的需求:用户希望按任意维度(如门店、时间、商品分类等)进行分组统计,同时选择不同的指标(如 GMV、订单数、客单价等)进行聚合计算。

这种需求如果直接写固定 SQL 会非常死板,而通过 FreeMarker 模板 动态构造 SQL,我们可以在保证安全的前提下,让维度、指标、过滤条件、排序、分页等全部可配置化,从而实现灵活的数据分析能力。


1. 需求建模

一个典型的数据透视分析需要定义三部分信息:

  1. 维度(dimensions)
    按哪些字段分组,如 shop_idsale_date 等。

  2. 指标(measures)
    对哪些字段做聚合计算,如 SUM(amount)COUNT(order_id)
    例如你提供的结构:

    "measures": [{ "func": "sum",   "field": "amount",   "alias": "gmv" },{ "func": "count", "field": "order_id", "alias": "orders" }
    ]
    
  3. 过滤条件(filters)
    如时间区间、门店、商品分类等限制。


2. 安全策略

动态 SQL 最大的风险是 SQL 注入,所以必须做到:

  • 列名白名单
    允许的字段映射表,如:

    Map<String, String> columnMap = Map.of("amount",   "t.amount","order_id", "t.order_id","shop_id",  "t.shop_id"
    );
    
  • 聚合函数白名单

    Set<String> aggs = Set.of("SUM", "COUNT", "AVG", "MIN", "MAX");
    
  • 值参数绑定
    所有值通过命名参数 :param 传入,不直接拼到 SQL 字符串中。


3. FreeMarker SQL 模板

下面是一个支持维度、指标、过滤、排序、分页的 FreeMarker 模板 pivot.ftl

SELECT<#-- 维度 --><#if dimensions?has_content><#list dimensions as d>${d.sql} AS ${d.alias}<#if d_has_next>,</#if></#list><#if measures?has_content>,</#if></#if><#-- 指标 --><#list measures as m>${m.sql} AS ${m.alias}<#if m_has_next>,</#if></#list>FROM ${table} t<#if whereClauses?has_content>
WHERE<#list whereClauses as w>(${w})<#if w_has_next> AND </#if></#list>
</#if><#if dimensions?has_content>
GROUP BY<#list dimensions as d>${d.sql}<#if d_has_next>,</#if></#list>
</#if><#if orderBy?has_content>
ORDER BY<#list orderBy as o>${o.sql} ${o.dir}<#if o_has_next>,</#if></#list>
</#if><#if limit??>
LIMIT :p_limit<#if offset??>OFFSET :p_offset</#if>
</#if>

4. Java 构造 SQL

在 Java 端,根据前端传入的 {func, field, alias} 格式,映射成模板可用的 SQL 片段。

List<Map<String, Object>> measureList = measures.stream().map(m -> {String agg = m.getFunc().toUpperCase();if (!aggs.contains(agg)) {throw new IllegalArgumentException("非法聚合函数: " + agg);}String col = Optional.ofNullable(columnMap.get(m.getField())).orElseThrow(() -> new IllegalArgumentException("非法列: " + m.getField()));String sqlExpr = agg + "(" + col + ")";return Map.of("name", m.getField(),"alias", m.getAlias(),"sql", sqlExpr);
}).toList();

这样,如果用户传入:

"measures": [{ "func": "sum",   "field": "amount",   "alias": "gmv" },{ "func": "count", "field": "order_id", "alias": "orders" }
]

模板渲染后就会生成:

SELECTSUM(t.amount) AS gmv,COUNT(t.order_id) AS orders
FROM order_table t

5. 加入维度

如果加上维度:

"dimensions": [{ "field": "shop_id", "alias": "shop" }
]

生成的 SQL 会是:

SELECTt.shop_id AS shop,SUM(t.amount) AS gmv,COUNT(t.order_id) AS orders
FROM order_table t
GROUP BY t.shop_id

6. 好处

  • 灵活性高:维度、指标、过滤条件全可配置。

  • 安全:列、函数、表等全部走白名单;值参数绑定,防止注入。

  • 可扩展:可以轻松加入条件聚合实现行转列(透视列)。

  • 通用性:同一套模板可支持不同业务场景(销售、库存、财务等)。


7. 总结

通过 FreeMarker 模板 + 白名单映射 + 参数绑定,我们可以优雅地实现一个安全、灵活的数据透视分析引擎。

核心要点:

  1. 模板只负责结构,不直接拼用户输入。

  2. Java 端负责校验、映射、生成 SQL 片段。

  3. 所有值用参数绑定,杜绝 SQL 注入。

这种方式不仅可以满足 ERP 复杂报表的需求,也能作为通用 BI 引擎的核心实现方案。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95775.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95775.shtml
英文地址,请注明出处:http://en.pswp.cn/diannao/95775.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

13.深度学习——Minst手写数字识别

第一部分——起手式 import torch from torchvision import datasets, transforms import torch.nn as nn import torch.nn.functional as F import torch.optim as optimuse_cuda torch.cuda.is_available()if use_cuda:device torch.device("cuda") else: device…

【JAVA高级】实现word转pdf 实现,源码概述。深坑总结

之前的需求做好后,需求,客户突发奇想。要将生成的word转为pdf! 因为不想让下载文档的人改动文档。 【JAVA】实现word添加标签实现系统自动填入字段-CSDN博客 事实上这个需求难度较高,并不是直接转换就行的 word文档当中的很多东西都需要处理 public static byte[] gener…

数据驱动测试提升自动化效率

测试工程师老王盯着满屏重复代码叹气&#xff1a;“改个搜索条件要重写20个脚本&#xff0c;这班加到啥时候是个头&#xff1f;” 隔壁组的小李探过头&#xff1a;“试试数据驱动呗&#xff0c;一套脚本吃遍所有数据&#xff0c;我们组上周测了300个组合都没加班&#xff01;”…

模板引用(Template Refs)全解析2

三、v-for 中的模板引用 当在 v-for 中使用模板引用时,引用的 value 会自动变为一个数组,包含列表中所有元素/组件的引用(需 Vue 3.5+ 版本,旧版需手动处理且顺序不保证)。 1. 基本用法(Vue 3.5+) <script setup> import { ref, useTemplateRef, onMounted } f…

【Linux系统】进程间通信:System V IPC——共享内存

前文中我们介绍了管道——匿名管道和命名管道来实现进程间通信&#xff0c;在介绍怎么进行通信时&#xff0c;我们有提到过不止管道的方式进行通信&#xff0c;还有System V IPC&#xff0c;今天这篇文章我们就来学习一下System V IPC中的共享内存1. 为何引入共享内存&#xff…

[优选算法专题二滑动窗口——最大连续1的个数 III]

题目链接 最大连续1的个数 III 题目描述 题目解析 问题本质 输入&#xff1a;二进制数组nums&#xff08;只包含 0 和 1&#xff09;和整数k操作&#xff1a;最多可以将k个 0 翻转成 1目标&#xff1a;找到翻转后能得到的最长连续 1 的子数组长度 这个问题的核心是要找到一…

C#单元测试(xUnit + Moq + coverlet.collector)

C#单元测试 xUnit Moq coverlet.collector 1.添加库 MlyMathLib 2.编写库函数内容 using System;namespace MlyMathLib {public interface IUserRepo{string GetName(int id);}public class UserService{private readonly IUserRepo _repo;public UserService(IUserRepo repo…

【数据库】Oracle学习笔记整理之五:ORACLE体系结构 - 参数文件与控制文件(Parameter Files Control Files)

Oracle体系结构 - 参数文件与控制文件&#xff08;Parameter Files & Control Files&#xff09; 参数文件与控制文件是Oracle数据库的“双核基石”&#xff1a;参数文件是实例的“启动配置中心”&#xff0c;定义运行环境与规则&#xff1b;控制文件是数据库的“物理元数据…

GDB典型开发场景深度解析

GDB典型开发场景深度解析 以下是开发过程中最常见的GDB使用场景&#xff0c;结合具体实例和调试技巧&#xff0c;帮助开发者高效解决实际问题&#xff1a;一、崩溃分析&#xff08;Core Dump调试&#xff09; 场景&#xff1a;程序突然崩溃&#xff0c;生成了core文件 # 启动调…

存储、硬盘、文件系统、 IO相关常识总结

目录 &#xff08;一&#xff09;存储 &#xff08;1&#xff09;定义 &#xff08;2&#xff09;分类 &#xff08;二&#xff09;硬盘 &#xff08;1&#xff09;容量&#xff08;最主要的参数&#xff09; &#xff08;2&#xff09;转速 &#xff08;3&#xff09;访…

docker安装mongodb及java连接实战

1.docker部署mongodb docker run --name mongodb -d -p 27017:27017 -v /data/mongodbdata:/data/db -e MONGO_INITDB_ROOT_USERNAMEtestmongo -e MONGO_INITDB_ROOT_PASSWORDtest123456 mongodb:4.0.112.项目实战 <dependencies><dependency><groupId>org.m…

Java设计模式之《工厂模式》

目录 1、介绍 1.1、定义 1.2、优缺点 1.3、使用场景 2、实现 2.1、简单工厂模式 2.2、工厂方法模式 2.3、抽象工厂模式 3、小结 前言 在面向对象编程中&#xff0c;创建对象实例最常用的方式就是通过 new 操作符构造一个对象实例&#xff0c;但在某些情况下&#xff0…

【异步】js中异步的实现方式 async await /Promise / Generator

JS的异步相关知识 js里面一共有以下异步的解决方案 传统的回调 省略 。。。。 生成器 Generator 函数是 ES6 提供的一种异步编程解决方案, 语法上&#xff0c;首先可以把它理解成&#xff0c;Generator 函数是一个状态机&#xff0c;封装了多个内部状态。执行 Generator 函数…

JVM字节码文件结构

Class文件结构class文件是二进制文件&#xff0c;这里要介绍的是这个二级制文件的结构。思考&#xff1a;一个java文件编译成class文件&#xff0c;如果要描述一个java文件&#xff0c;需要哪些信息呢&#xff1f;基本信息&#xff1a;类名、父类、实现哪些接口、方法个数、每个…

11.web api 2

5. 操作元素属性 5.1操作元素常用属性 &#xff1a;通过 JS 设置/修改标签元素属性&#xff0c;比如通过 src更换 图片最常见的属性比如&#xff1a; href、title、src 等5.2 操作元素样式属性 &#xff1a;通过 JS 设置/修改标签元素的样式属性。使用 className 有什么好处&a…

java中数组和list的区别是什么?

在Java中&#xff0c;数组&#xff08;Array&#xff09;和List&#xff08;通常指java.util.List接口的实现类&#xff0c;如ArrayList、LinkedList&#xff09;是两种常用的容器&#xff0c;但它们在设计、功能和使用场景上有显著区别。以下从核心特性、使用方式等方面详细对…

Python爬取推特(X)的各种数据

&#x1f31f; Hello&#xff0c;我是蒋星熠Jaxonic&#xff01; &#x1f308; 在浩瀚无垠的技术宇宙中&#xff0c;我是一名执着的星际旅人&#xff0c;用代码绘制探索的轨迹。 &#x1f680; 每一个算法都是我点燃的推进器&#xff0c;每一行代码都是我航行的星图。 &#x…

Oracle数据库文件管理与空间问题解决指南

在Oracle数据库运维中&#xff0c;表空间、数据文件及相关日志文件的管理是保障数据库稳定运行的核心环节。本文将系统梳理表空间与数据文件的调整、关键文件的移动、自动扩展配置&#xff0c;以及常见空间不足错误的排查解决方法&#xff0c;为数据库管理员提供全面参考。 一、…

华为实验综合小练习

描述&#xff1a; 1 内网有A、B、C 三个部门。所在网段如图所示。 2 内网服务器配置静态IP,网关192.168.100.1。 3 sw1和R1之间使用vlan200 192.168.200.0/30 互联。 4 R1向运营商申请企业宽带并申请了5个公网IP&#xff1a;200.1.1.1-.5子网掩码 255.255.255.248&#xff0c;网…

Flink面试题及详细答案100道(1-20)- 基础概念与架构

《前后端面试题》专栏集合了前后端各个知识模块的面试题&#xff0c;包括html&#xff0c;javascript&#xff0c;css&#xff0c;vue&#xff0c;react&#xff0c;java&#xff0c;Openlayers&#xff0c;leaflet&#xff0c;cesium&#xff0c;mapboxGL&#xff0c;threejs&…