CTE聚合查询,性能优化不止10几倍

太好了!这才是生产里最常见的真实场景:

你根本不知道前端会传哪些 company_id,

而是要先从某张表(比如:当月有业务的活跃公司、当月有上报数据的公司、权限表、组织架构表......)动态查出一堆 company_id,

然后拿这堆 company_id 去算前面说的批量聚合报表。

这时候就不能再用 VALUES / UNNEST 硬写公司列表了,必须把「公司ID的来源」也写成一个查询。

最优雅、最高效、永远不会写错的终极模板(直接抄走)

sql 复制代码
WITH 
-- 第1步:动态查出本次需要计算的公司ID(或主表ID)列表
target_companies AS (
    SELECT DISTINCT company_id
    FROM active_company_report        -- 比如:当月上报过的公司
    WHERE report_month = '2025-11'
      AND status = '已审核'
    
    -- 或者更复杂的业务逻辑:
    -- SELECT org_id AS company_id FROM org_dimension WHERE level = 2 AND is_deleted = 0
    -- UNION
    -- SELECT company_id FROM special_white_list WHERE month = '2025-11'
),

-- 第2步:把月份参数也只出现一次(可选,但强烈推荐)
params AS (
    SELECT '2025-11' AS query_month
),

-- 第3步:核心!用 target_companies 替代原来手写的公司列表
base_main AS (
    SELECT 
        p.query_month,
        tc.company_id,
        m.*                                      -- 主表所有字段
    FROM target_companies tc
    CROSS JOIN params p                      -- 广播月份(只有1行)
    LEFT JOIN main_table m
           ON m.month = p.query_month
          AND m.company_id = tc.company_id
    -- 如果有公司当月在主表根本没数据,这里也会补出一行 NULL,后面看你是否需要
),

-- 第4步:后面的所有子查询 CTE 完全不变!(和之前模板一模一样)
sale_summary AS (
    SELECT 
        bm.company_id,
        SUM(s.amount)   AS sale_amt,
        COUNT(*)        AS sale_cnt
    FROM base_main bm
    JOIN sale s ON s.main_id = bm.main_id
    GROUP BY bm.company_id
),

purchase_summary AS (
    SELECT bm.company_id, SUM(p.amount) AS purchase_amt
    FROM base_main bm
    JOIN purchase p ON p.main_id = bm.main_id
    GROUP BY bm.company_id
),

-- 最终结果
final_result AS (
    SELECT 
        bm.query_month,
        bm.company_id,
        COALESCE(s.sale_amt, 0)        AS sale_amt,
        COALESCE(p.purchase_amt, 0)    AS purchase_amt,
        COALESCE(s.sale_amt, 0) - COALESCE(p.purchase_amt, 0) AS profit
    FROM base_main bm
    LEFT JOIN sale_summary s      ON s.company_id = bm.company_id
    LEFT JOIN purchase_summary p  ON p.company_id = bm.company_id
)

SELECT * FROM final_result
ORDER BY company_id;

这种写法的 6 大终极优势(生产必备)

优势 说明
公司列表动态生成,永远不用前端传了 前端只传月份就行,后端自动算出当月该算哪些公司
支持任意复杂筛选逻辑 target_companies 里可以写 10 张表 JOIN、窗口函数、UNION ALL 随便玩
即使主表里某些公司当月没数据,也会补一行(方便前端展示0) LEFT JOIN main_table 保证每家公司都有占位行
后续加新维度(比如区域、业务线)只改 target_companies 一处 真正的"单一职责"写法
性能极高 target_companies 通常只有几十~几千行,CROSS JOIN 成本几乎为 0
可读性爆炸 任何人一看就知道:先算要报哪些公司 → 再去拉数据 → 再聚合

进阶变种(你以后一定会用到)

sql 复制代码
-- 变种1:同时支持"前端手动指定某些公司" + "自动补全其他活跃公司"
target_companies AS (
    -- 前端指定要算的(优先级最高)
    SELECT company_id FROM (VALUES (1001),(1005),(9999)) t(id)  
    UNION  
    -- 再自动补全当月其他活跃但前端没传的公司
    SELECT DISTINCT company_id 
    FROM active_company_report 
    WHERE report_month = '2025-11'
      AND company_id NOT IN (1001,1005,9999)
),

-- 变种2:直接算到「部门/项目/门店」粒度(完全一样结构)
target_units AS (
    SELECT project_id AS unit_id, project_name
    FROM project_dimension 
    WHERE start_date <= '2025-11' AND (end_date >= '2025-11' OR end_date IS NULL)
)
-- 后面的 base_main 把 company_id 换成 unit_id 就行

把你现在「查公司ID列表」的SQL贴出来,我30秒帮你和批量聚合报表无缝拼在一起,保证一次成型!

相关推荐
老青蛙1 小时前
Easy Work-简单、易用、傻瓜式的 Java 流程引擎
java·开源
茶杯6751 小时前
“舒欣双免“方案助力MSI-H/dMMR结肠癌治疗新突破
java·服务器·前端
我真会写代码1 小时前
从入门到精通:Java Socket 网络编程实战(含线程池优化)
java·linux·服务器·socket·tcp/ip协议
BBB努力学习程序设计1 小时前
Java:理解数据类型和变量
java
亭上秋和景清1 小时前
数据在内存中的存储
java·开发语言
古城小栈1 小时前
SpringBoot:声明式事务 和 编程式事务 的擂台霸业
java·spring boot·后端
小二·1 小时前
Java基础教程之网络编程
java·开发语言·网络
泥嚎泥嚎1 小时前
【Android】RecyclerView 刷新方式全解析:从 notifyDataSetChanged 到 DiffUtil
android·java
努力学算法的蒟蒻1 小时前
day23(12.3)——leetcode面试经典150
java