SQL Server 根据 custId 合并多个表的数据
支付阿杰
结论
不需要连接三个表。用 UNION ALL 把各表中同一客户的数据合并成多行即可。每个查询分支返回相同的列,并用固定值标明数据来源。
例如:
col1 = 1:来自Customercol1 = 2:来自Paymentcol1 = 3:来自Refund
查询示例
下面的查询返回 custId = 3 的客户资料、付款备注和退款备注:
DECLARE @CustId int = 3;
SELECT
c.CustId,
c.Notes,
1 AS col1
FROM Customer AS c
WHERE c.CustId = @CustId
UNION ALL
SELECT
p.custId,
p.PmtNotes,
2 AS col1
FROM Payment AS p
WHERE p.custId = @CustId
AND p.PmtNotes IS NOT NULL
UNION ALL
SELECT
r.custId,
r.refNotes,
3 AS col1
FROM Refund AS r
WHERE r.custId = @CustId
AND r.refNotes IS NOT NULL
ORDER BY col1;
根据题目中的示例数据,查询结果是:
| CustId | Notes | col1 |
|---|---|---|
| 3 | gg | 1 |
| 3 | jj | 3 |
Payment 表中 custId = 3 对应的 PmtNotes 为 NULL,所以被 PmtNotes IS NOT NULL 条件排除了。如果想保留这条记录,删除该条件即可。结果中会增加一行:
| CustId | Notes | col1 |
|---|---|---|
| 3 | NULL | 2 |
为什么使用 UNION ALL
这里要把不同表中的备注合并成多行,而不是连接到同一行,因此用 UNION ALL 更合适。
三个查询分支返回的内容都是:
客户编号、备注、来源标识
原始列名虽然分别是 Notes、PmtNotes 和 refNotes,但这些列在各个查询中处于相同位置。合并后的结果会采用第一个查询指定的列名 Notes。
这里应使用 UNION ALL,不建议直接使用 UNION。UNION 会执行额外的去重操作,可能删掉原本有效的重复记录,也会带来不必要的处理开销。
如果只需要 Customer 和 Refund
如果不需要付款记录,只合并两个表即可:
DECLARE @CustId int = 3;
SELECT
c.CustId,
c.Notes,
1 AS col1
FROM Customer AS c
WHERE c.CustId = @CustId
UNION ALL
SELECT
r.custId,
r.refNotes,
3 AS col1
FROM Refund AS r
WHERE r.custId = @CustId
AND r.refNotes IS NOT NULL
ORDER BY col1;
这段查询会返回题目所示的两行结果。
返回更清晰的来源名称
数字标识便于程序处理,但人工查看时不够直观。可以在结果中同时返回来源名称:
DECLARE @CustId int = 3;
SELECT
c.CustId,
c.Notes,
1 AS SourceId,
CAST('Customer' AS varchar(20)) AS SourceName
FROM Customer AS c
WHERE c.CustId = @CustId
UNION ALL
SELECT
p.custId,
p.PmtNotes,
2 AS SourceId,
CAST('Payment' AS varchar(20)) AS SourceName
FROM Payment AS p
WHERE p.custId = @CustId
AND p.PmtNotes IS NOT NULL
UNION ALL
SELECT
r.custId,
r.refNotes,
3 AS SourceId,
CAST('Refund' AS varchar(20)) AS SourceName
FROM Refund AS r
WHERE r.custId = @CustId
AND r.refNotes IS NOT NULL
ORDER BY SourceId;
注意事项
参与 UNION ALL 的每个 SELECT 都必须满足以下条件:
- 返回的列数相同。
- 对应位置的列使用相同或兼容的数据类型。
Notes、PmtNotes 和 refNotes 最好使用兼容的字符类型,例如都使用 nvarchar,长度也应相容。如果这些列的数据类型或排序规则不同,可能需要显式转换:
CAST(p.PmtNotes AS nvarchar(500))
数据量较大时,还要检查三个表的客户编号列是否有合适的索引。Customer.CustId 通常应设为主键,Payment.custId 和 Refund.custId 则可以建立普通索引,以提高按客户编号筛选的效率。
备注:内容仅供参考。
