我有 3 个表需要连接,其中 2 个对于计算值并在第三个表中提供它很重要,问题是在这 2 个表中我有 2 列需要分组以进行计算。
这些是表格 - 第一个表格:
SELECT customerId, baseCurrencyId, SUM(amount) AS totalRecovered
FROM dbo.RecoveredDebt
WHERE (isDeleted = 0)
GROUP BY customerId, baseCurrencyId;
首次查询结果:
客户ID | 基础货币ID | 已恢复总数 |
---|---|---|
2 | 1 | 950 |
4 | 1 | 10 |
1 | 2 | 100 |
2 | 2 | 500 |
第二张表:
SELECT customerId, baseCurrencyId, SUM(amount) AS totalOld
FROM dbo.OldDebt
WHERE (isDeleted = 0)
GROUP BY customerId, baseCurrencyId;
第二次查询结果:
客户ID | 基础货币ID | 总旧 |
---|---|---|
2 | 1 | 2000 |
1 | 2 | 100 |
2 | 2 | 300 |
现在技巧来了,我想从
Customer
表中进行选择并获取这些列:
id (customer id),
baseCurrencyId,
totalOldDebt (from first query),
totalRecoveredDebt (from second query),
totalDebt (totalOldDebt - totalRecoveredDebt)
但我希望它按
baseCurrencyId
进行分组,例如,如果客户有超过一种货币的债务,则必须对每种货币进行计算,如果一个表中存在包含一种货币的行,但另一个表中没有那么该值必须为零并且计算仍然会发生。
这是我根据提供的表格计算后的预期结果:
id | 基础货币ID | 旧债总额 | 已收回债务总额 | 债务总额 |
---|---|---|---|---|
1 | 2 | 100 | 100 | 0 |
2 | 1 | 2000 | 950 | 1050 |
2 | 2 | 300 | 500 | -200 |
4 | 1 | 0 | 10 | -10 |
我尝试过使用 CTE,但我不知道该怎么做:
WITH OldCTE AS
(
SELECT
customerId, baseCurrencyId, SUM(amount) AS totalOld
FROM
dbo.OldDebt
WHERE
(isDeleted = 0)
GROUP BY
customerId, baseCurrencyId
),
RecoveredCTE AS
(
SELECT
customerId, baseCurrencyId, SUM(amount) AS totalRecovered
FROM
dbo.RecoveredDebt
WHERE
(isDeleted = 0)
GROUP BY
customerId, baseCurrencyId
)
SELECT C.id
FROM Customer AS C
LEFT JOIN OldCTE AS OCTE ON OCTE.customerId = C.id
LEFT JOIN RecoveredCTE AS RCTE ON RCTE.customerId = C.id
如果您想使用 CTE 作为变体:
WITH OldCTE AS (
SELECT customerId, baseCurrencyId, SUM(amount) AS totalOld
FROM dbo.OldDebt
WHERE (isDeleted = 0)
GROUP BY customerId, baseCurrencyId
), RecoveredCTE AS (
SELECT customerId, baseCurrencyId, SUM(amount) AS totalRecovered
FROM dbo.RecoveredDebt
WHERE (isDeleted = 0)
GROUP BY customerId, baseCurrencyId
)
SELECT C.id,
coalesce(OCTE.totalOld, 0) as totalOldDebt,
coalesce(RCTE.totalRecovered, 0) as totalRecoveredDebt,
coalesce(OCTE.totalOld, 0) - coalesce(RCTE.totalRecovered, 0) as totalDebt
FROM OldCTE AS OCTE
FULL JOIN RecoveredCTE AS RCTE ON RCTE.customerId = OCTE.customerId and OCTE.baseCurrencyId = RCTE.baseCurrencyId
JOIN Customer as C on C.id = coalesce(OCTE.customerId, RCTE.customerId)
附注我对 sql-server 不太熟悉,但你可能可以在没有 CTE 的情况下编写它:
select
C.id,
coalesce(OD.baseCurrencyId, RD.baseCurrencyId) as currency,
coalesce(sum(OD.amount), 0) as totalOldDebt,
coalesce(sum(RD.amount), 0) as totalRecoveredDebt,
coalesce(sum(OD.amount), 0) - coalesce(sum(RD.amount), 0) as totalDebt
from dbo.OldDebt as OD
full join dbo.RecoveredDebt as RD on OD.customerId = RD.customerId and OD.baseCurrencyId = RD.baseCurrencyId and RD.isDeleted = 0
join Customer as C on C.id = coalesce(OD.customerId, RD.customerId)
where OD.customerId is null or OD.isDeleted = 0
group by C.id, coalesce(OD.baseCurrencyId, RD.baseCurrencyId)
我也不确定这是否更优化,因为它需要检查查询计划。
您需要首先在旧债务和已收回债务之间执行
FULL OUTER JOIN
,收集值,然后与您的客户结合起来。这可以在子选择或另一个 CTE 中完成。 (...或者,正如 Gwaeren 在我之前的另一个答案中发布的那样,只需重新排序连接即可。)
WITH OldCTE AS (
SELECT customerId, baseCurrencyId, SUM(amount) AS totalOldDebt
FROM dbo.OldDebt
WHERE (isDeleted = 0)
GROUP BY customerId, baseCurrencyId
), RecoveredCTE AS (
SELECT customerId, baseCurrencyId, SUM(amount) AS totalRecoveredDebt
FROM dbo.RecoveredDebt
WHERE (isDeleted = 0)
GROUP BY customerId, baseCurrencyId
), MergedCTE AS (
SELECT
COALESCE(R.customerId, O.customerId) AS customerId,
COALESCE(R.baseCurrencyId, O.baseCurrencyId) AS baseCurrencyId,
ISNULL(O.totalOldDebt, 0) AS totalOldDebt,
ISNULL(R.totalRecoveredDebt, 0) AS totalRecoveredDebt
FROM OldCTE O
FULL OUTER JOIN RecoveredCTE R
ON R.customerId = O.customerId
AND R.baseCurrencyId = O.baseCurrencyId
)
SELECT
C.Id,
M.baseCurrencyId,
M.totalOldDebt,
M.totalRecoveredDebt,
M.totalOldDebt - M.totalRecoveredDebt AS totalDebt
FROM Customer AS C
JOIN MergedCTE M
ON M.customerId = C.Id
ORDER BY
C.id,
M.baseCurrencyId
结果:
身份证 | 基础货币ID | 旧债总额 | 已收回债务总额 | 债务总额 |
---|---|---|---|---|
1 | 2 | 100 | 100 | 0 |
2 | 1 | 2000 | 950 | 1050 |
2 | 2 | 300 | 500 | -200 |
4 | 1 | 0 | 10 | -10 |
请参阅 this db<>fiddle 进行演示。
附录:您询问了另一条评论,是的,
UNION
是另一种方法:
WITH UnionCTE AS (
SELECT
customerId,
baseCurrencyId,
SUM(old) AS totalOldDebt,
SUM(recovered) AS totalRecoveredDebt
FROM (
SELECT customerId, baseCurrencyId, amount AS old, 0 as recovered
FROM dbo.OldDebt
WHERE (isDeleted = 0)
UNION ALL
SELECT customerId, baseCurrencyId, 0 AS old, amount as recovered
FROM dbo.RecoveredDebt
WHERE (isDeleted = 0)
) U
GROUP BY customerId, baseCurrencyId
)
SELECT C.id,
UCTE.baseCurrencyId, UCTE.totalOldDebt, UCTE.totalRecoveredDebt,
UCTE.totalOldDebt - UCTE.totalRecoveredDebt AS totalDebt
FROM Customer AS C
JOIN UnionCTE AS UCTE ON UCTE.customerId = C.id
ORDER BY C.Id, UCTE.baseCurrencyId
参见这个数据库<>小提琴。
还有其他方法。如果您的实际查询只需要访问一小部分客户(或者可能一次只访问一个客户),那么使用仅访问与所选客户关联的债务行的
CROSS APPLY
来计算债务可能是有意义的(s)。将您的客户 ID 预选到临时表(或表变量)中并使用它来预过滤债务数据也是一种选择。