尝试 LEFT JOIN 表时数量翻倍
Posted
技术标签:
【中文标题】尝试 LEFT JOIN 表时数量翻倍【英文标题】:Amount doubles when trying to LEFT JOIN tables 【发布时间】:2017-08-07 19:11:15 【问题描述】:我有 3 张桌子
客户表
CustomerID, LastName,
交易表
TransID, CustomerID, Price,
付款表
PaymentID, CustomerID, Paid,
我正在尝试通过组合 CustomerID
和 Price
和 Paid
进行查询以获得总余额,然后执行 Price - Paid
并获得总余额。
我试过了:
SELECT [Customers].LastName, SUM(Transactions.Price) AS [Total Price], SUM(Payments.Paid) AS [Total Paid], SUM(Transactions.Price - Payments.Paid) AS Balance
FROM ([Customers] LEFT JOIN Payments ON Payments.CustomerID = [Customers].CustomerID) LEFT JOIN Transactions ON Transactions.CustomerID = [Customers].CustomerID
GROUP BY [Customers].LastName;
但是Paid
的总金额是实际付款金额的两倍(例如: 如果客户有 150 美元的余额并支付了 65 美元,它会显示我支付的总金额 = 130 美元,结果是将使总余额为 20 美元而不是 85 美元)。
查询结果
作为旁注,Total Balance
列仅在已经付款的情况下才给我余额。如果尚未为特定的 CustomerID 付款,则它是空白的,而不是当前余额。
请帮忙!!!
【问题讨论】:
这看起来像是重复的问题。您的 [Customers] 表中是否存在重复的客户 ID? 不,只有1个 你能展示一个你的表格内容的小例子吗? 您可能拥有多个 id 的地方。 尝试使用 UNION,然后使用 SUM。我在回答中给出了一个例子。我已经测试过了。 【参考方案1】:您可以尝试以下查询:
SELECT [A].LastName, (SUM(A.PaidAmt) - SUM(PriceAmt) ) AS Amt
FROM (
SELECT
[Customers].LastName, (Payments.Paid) as PaidAmt , 0 AS PriceAmt
FROM [Customers]
LEFT JOIN Payments
ON Payments.CustomerID = [Customers].CustomerID
UNION
SELECT
[Customers].LastName, 0 as PaidAmt , (Transactions.Price) AS PriceAmt
FROM [Customers]
LEFT JOIN Transactions
ON Transactions.CustomerID = [Customers].CustomerID
) A
GROUP BY A.LastName;
这应该可行。
【讨论】:
工作!!!但显示借方为贷方(借方用 ($20.00) 包围)贷方为没有 20.00 美元的借方 - 你能解决吗?谢谢! 这样(SUM(PriceAmt) - SUM(A.PaidAmt)) 即使没有付款也应该显示价值。由于使用了 LEFT OUTER 连接,它必须在所有情况下都有效,您应该会看到价值。【参考方案2】:为什么你会得到两倍、三倍或更多的金额?问题是基数之一。如果付款和交易都是与客户的 1:M 关系;并且它们之间除了客户之外没有任何关系,那么当您不希望它们重复时,记录就会被复制。假设我们有以下 # 条记录都与客户 1 相关
-
客户:1
付款:2
交易次数:2
当连接发生时你认为发生的是:
+------------+----------+---------------+-------+-----------+------+--+-------+
| CustomerID | LastName | TransactionID | Price | PaymentID | Paid | | Total |
+------------+----------+---------------+-------+-----------+------+--+-------+
| 1 | Smith | 1 | 150 | 1 | 20 | | |
| 1 | Smith | 3 | 30 | 3 | 40 | | |
| | | | 180 | | 60 | | 120 |
实际发生的情况是:(引擎不知道如何将付款与交易联系起来,因此它必须将每笔交易与所有付款联系起来!)
+------------------------------------------------------------------------------
| CustomerID | LastName | TransactionID | Price | PaymentID | Paid | | Total |
| 1 | Smith | 1 | 150 | 1 | 20 | | |
| 1 | Smith | 1 | 150 | 3 | 40 | | |
| 1 | smith | 3 | 30 | 1 | 20 | | |
| 1 | smith | 3 | 30 | 3 | 40 | | 240 |
+------------+----------+---------------+-------+-----------+------+--+-------+
请注意,每个交易 ID 都必须与付款 ID 配对,因此记录和金额会增加一倍、三倍或四倍,具体取决于解决交易和付款之间的 M:M 所需的记录数。实际上,由于没有定义任何关系,因此该表将所有客户支付记录交叉连接到所有客户交易记录。
现在,如果您有办法将付款与交易联系起来,从而形成 1:1 的关系,那么我们就不需要子查询了。但是,由于我怀疑您是否可以这样做(客户可以进行部分付款;因此您需要支持 1:M);我建议在连接之前在一个内联视图中进行总结。
当您加入这三个表时,您会得到 4 条记录,而不是 2 条。1 条客户记录 * 2 条付款记录 * 2 笔交易 = 4。现在,每笔交易中的每条记录都重复一次付款,因此如果 2 笔交易发生两次 10 美元的付款存在。要解决此问题,您必须在连接之前汇总记录;从而建立了 1:1:1 的关系,并且人为夸大的计数/总数消失了。
请注意:您需要nz
(取第一个非空值)作为总价和总支付的金额,包括没有为客户付款或交易的情况。假设客户一没有付款,当您取 150+30 时,您将得到 180 减去 null 并且您得到 null。因此,新西兰对于处理客户未发生付款或交易的情况非常重要。
SELECT c.LastName
, nz(T.[Total Price],0)
, nz(P.[Total Paid],0)
, nz(T.[Total Price],0)- nz(P.[Total Paid],0) AS Balance
FROM [Customers] c
LEFT JOIN (SELECT sum(Payments.paid) as [Total Paid], customerID
FROM payments
GROUP BY customerID ) p
ON P.CustomerID = c.CustomerID
LEFT JOIN (SELECT sum(Transactions.price) as [Total Price], customerID
FROM Transactions
GROUP BY customerID) t
ON t.CustomerID = c.CustomerID
【讨论】:
【参考方案3】:这是绝对符合您要求的查询
select a.lastname,a.tot_payment,b.tot_trans,(b.tot_trans-a.tot_payment) as balance
from (select c.id,lastname,sum(p.price) tot_payment--,sum(t.price)
from customers c
left join payments p on c.id = p.custid
group by lastname,c.id) a
join
(select c.id,sum(t.price) tot_trans
from customers c
left join transactions t on t.custid = c.id
group by c.id)b
on a.id = b.id
【讨论】:
以上是关于尝试 LEFT JOIN 表时数量翻倍的主要内容,如果未能解决你的问题,请参考以下文章