太好了!这才是生产里最常见的真实场景:
你根本不知道前端会传哪些 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秒帮你和批量聚合报表无缝拼在一起,保证一次成型!