尝试 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,

我正在尝试通过组合 CustomerIDPricePaid 进行查询以获得总余额,然后执行 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 表时数量翻倍的主要内容,如果未能解决你的问题,请参考以下文章

inner join 还是 left join 什么区别啊

mysql 优化慢复杂sql (多个left join 数量过大 order by 巨慢)

EF之外键Include() left join

[易飞]自动生产采购发票开票数量翻倍(缺乏事务管理缺陷)

[易飞]自动生产采购发票开票数量翻倍(缺乏事务管理缺陷)

加入发货后订单数量翻倍