20多个表,每个表都有userId 和几个属性,要把他们全部都拼到一张表,怎么处理?
按 userId 汇总关联 20 张表。
面对 20多张表 的全量字段合并(宽表化),这是一个 ETL(抽取、转换、加载) 过程。
如果直接用 JOIN 拼 20 张表,上百万行数据在执行时会产生巨大的笛卡尔积风险,且极易触发 MySQL 的溢出或者超时。
方案一:SQL 离线宽表化(推荐)
采用 "主表 + 逐步左连接" 的方式,分步写入一张中间宽表。
1. 创建目标宽表(大宽表)
先创建一个包含所有字段的宽表。
注意:关联字段必须全部建有索引。
这 20 张表参与关联的 user_id 必须全部建有索引。
sql
CREATE TABLE tmp_user_profile_20260414 AS
SELECT user_id FROM tmp_data_20260414;
ALTER TABLE tmp_user_profile_20260414 ADD PRIMARY KEY (user_id);
2. 分步更新(分而治之)
每张表单独更新到宽表中,这样即使某张表数据有问题,也不会影响整体流程。
使用 sql 的 Update Join 处理。
sql
-- 拼入数据
UPDATE tmp_user_profile_20260414 w
JOIN (SELECT user_id, AVG(diff) as avg_sleep FROM t_sleep GROUP BY user_id) s
ON w.user_id = s.user_id
SET w.avg_sleep_seconds = s.avg_sleep;
-- 重复上述步骤,直到 20 张表全部拼完
方案二:Java 内存并行映射(高性能,适合逻辑复杂)
如果某些表的属性需要复杂的 Java 逻辑计算,建议在后端处理:
- 定义 Map 容器:
Map<Long, UserProfileDTO> profileMap。 - 并行查询:使用
CompletableFuture并行开启 20 个任务,每个任务查一张表并按user_id塞入 Map。 - 批量写入:拼装完成后,利用 MyBatis 的
batchInsert一次性落库。