SQLazy:多表按 ID 合并为单行
问题描述
将多个结构相似但列名不同的表按 ID 全外连接合并为宽表。4 个表结构类似,每个表 2 个字段,字段含义相同但名字不同(id、id2、id3、id4 都是 ID)。现在要将 4 个表按 ID 合并为单行,每个 ID 唯一一行,当 ID 不在某个表中时对应列取 NULL。
源数据
T1 table:
id |
colA |
555 |
A_OK |
T2 table:
id2 |
colB |
555 |
B_OK |
10 |
Bx |
T3 table:
id3 |
colC |
222 |
C |
T4 table:
id4 |
colD |
10 |
Dx |
期望结果
ID_main |
id |
colA |
id2 |
colB |
id3 |
colC |
id4 |
colD |
10 |
10 |
Bx |
10 |
Dx |
||||
222 |
222 |
C |
||||||
555 |
555 |
A_OK |
555 |
B_OK |
比如,ID=555 在 T1 和 T2 中都有记录,T3 和 T4 中没有,因此 id、colA、id2、colB 有值,id3/colC/id4/colD 为 NULL。
ID=222 只在 T3 中有记录,其他表都没有,因此只有 id3、colC 有值,其余均为 NULL。
ID=10 在 T2 和 T4 中有记录,T1 和 T3 中没有,因此 id2、colB、id4、colD 有值,其余为 NULL。
SQLazy 分步实现
核心思路:先用 derive 将各表的 ID 列统一命名为 ID_main,方便后续合并。然后从第一个表开始,逐表进行全外连接:每次用 join 将当前结果与下一个表按 ID_main 全外连接,再用 derive 和 nvl 函数将新增 ID 合并到 ID_main 列,如此逐表追加获得结果。
Name |
Anchor |
Statement |
t1 |
T1 |
derive id as ID_main, id, colA |
t2 |
T2 |
derive id2 as ID_main, id2, colB |
t3 |
T3 |
derive id3 as ID_main, id3, colC |
t4 |
T4 |
derive id4 as ID_main, id4, colD |
r1 |
t1 |
join ID_main; with t2; ID_main; take id2, colB; full |
r1b |
derive nvl(ID_main, id2) as ID_main, id, colA, id2, colB |
|
r2 |
join ID_main; with t3; ID_main; take id3, colC; full |
|
r2b |
derive nvl(ID_main, id3) as ID_main, id, colA, id2, colB, id3, colC |
|
r3 |
join ID_main; with t4; ID_main; take id4, colD; full |
|
derive nvl(ID_main, id4) as ID_main, id, colA, id2, colB, id3, colC, id4, colD |
下面逐一解释这些步骤。
第 1–4 步:统一各表的 ID 列名
derive id as ID_main, id, colA
对 T1–T4 分别用 derive 将各自不同的 ID 列名(id/id2/id3/id4)统一重命名为 ID_main。

第 5 步:全外连接 T1 和 T2
join ID_main; with t2; ID_main; take id2, colB; full
用 join 功能将 t1 与 t2 按 ID_main 进行全外连接。

第 6 步:合并 ID 列中的 NULL
derive nvl(ID_main, id2) as ID_main, id, colA, id2, colB
如果某条记录来自 t2 而 t1 中没有,则这条记录的 ID_main 为 NULL,这一步将把这种记录中的 ID_main 赋值为 t2.id2,从而确保 ID_main 列始终有值。不同的 SQL 实现本步骤时方言不同,比如 oracle 用 nvl,SQLServer 用 COALESCE,而 NSPL 统一使用 nvl 函数,将来编译时将根据数据库类型自动翻译成相应的方言。

第 7–8 步:重复追加 T3
join ID_main; with t3; ID_main; take id3, colC; full
derive nvl(ID_main, id3) as ID_main, id, colA, id2, colB, id3, colC
重复 join + derive 模式,将 T3 全外连接到当前结果中。每次追加一个表后,用 ifn 将新增的 ID 合并到统一的 ID_main 列。

第 9-10 步:重复追加 T4,得到最终结果
join ID_main; with t4; ID_main; take id4, colD; full
derive nvl(ID_main, id4) as ID_main, id, colA, id2, colB, id3, colC, id4, colD

编译生成 SQL
确认上述步骤后,SQLazy 编译器自动生成原生 SQL(SQL Server 语法):
WITH t1 AS (
SELECT id AS ID_main, id, colA
FROM T1
),
t2 AS (
SELECT id2 AS ID_main, id2, colB
FROM T2
),
r1 AS (
SELECT t1.ID_main, t1.id, t1.colA, t2.id2, t2.colB
FROM t1
FULL JOIN t2 ON ID_main = t2.ID_main
),
r1b AS (
SELECT COALESCE(NULLIF(CAST(ID_main AS VARCHAR(10)), ''), NULLIF(CAST(id2 AS VARCHAR(10)), '')) AS ID_main
, id, colA, id2, colB
FROM r1
),
t3 AS (
SELECT id3 AS ID_main, id3, colC
FROM T3
),
r2 AS (
SELECT r1b.ID_main, r1b.id, r1b.colA, r1b.id2, r1b.colB
, t3.id3, t3.colC
FROM r1b
FULL JOIN t3 ON ID_main = t3.ID_main
),
r2b AS (
SELECT COALESCE(NULLIF(CAST(ID_main AS VARCHAR(10)), ''), NULLIF(CAST(id3 AS VARCHAR(10)), '')) AS ID_main
, id, colA, id2, colB, id3
, colC
FROM r2
),
t4 AS (
SELECT id4 AS ID_main, id4, colD
FROM T4
),
r3 AS (
SELECT r2b.ID_main, r2b.id, r2b.colA, r2b.id2, r2b.colB
, r2b.id3, r2b.colC, t4.id4, t4.colD
FROM r2b
FULL JOIN t4 ON ID_main = t4.ID_main
)
SELECT COALESCE(NULLIF(CAST(ID_main AS VARCHAR(10)), ''), NULLIF(CAST(id4 AS VARCHAR(10)), '')) AS ID_main
, id, colA, id2, colB, id3
, colC, id4, colD
FROM r3
SQLazy 让你用业务语言描述逻辑,而不是用 SQL 语法写嵌套查询。SQLazy 的分步计算将多表合并拆解为统一列名、逐表全外连接、合并 ID 等多个独立步骤,每一步都可以独立验证中间结果。join 功能配合 derive 和 nvl 函数,可以灵活地处理合并后的数据,如重命名列、合并 NULL 值等。这种逐个追加表的策略比 SQL 中编写多个 FULL JOIN + COALESCE 的嵌套查询更加清晰可维护。
官方链接
SQLazy 在线体验:sqlazy.com(免费,无需注册)
SQLazy 项目仓库:github.com/SPLWare/SQLazy

英文版 https://c.esproc.com/article/1784109636536