用 FreeMarker 動態構造 SQL 實現數據透視分析

在 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&…