SQL JOIN,GROUP BY在三个表上获取总计

时间:2009-08-06 23:41:04

标签: sql join aggregate

我继承了以下数据库设计。表格是:

customers
---------
customerid  
customernumber

invoices
--------
invoiceid  
amount

invoicepayments
---------------
invoicepaymentid  
invoiceid  
paymentid

payments
--------
paymentid  
customerid  
amount

我的查询需要返回给定客户编号的invoiceid,发票金额(在发票表中)和应付金额(发票金额减去对发票的任何付款)。客户可能有多张发票。

以下查询在向发票进行多次付款时向我提供了重复记录:

SELECT i.invoiceid, i.amount, i.amount - p.amount AS amountdue
FROM invoices i
LEFT JOIN invoicepayments ip ON i.invoiceid = ip.invoiceid
LEFT JOIN payments p ON ip.paymentid = p.paymentid
LEFT JOIN customers c ON p.customerid = c.customerid
WHERE c.customernumber = '100'

我该如何解决这个问题?

5 个答案:

答案 0 :(得分:13)

我不确定我能不能找到你,但这可能就是你想要的:

SELECT i.invoiceid, sum(case when i.amount is not null then i.amount else 0 end), sum(case when i.amount is not null then i.amount else 0 end) - sum(case when p.amount is not null then p.amount else 0 end) AS amountdue
FROM invoices i
LEFT JOIN invoicepayments ip ON i.invoiceid = ip.invoiceid
LEFT JOIN payments p ON ip.paymentid = p.paymentid
LEFT JOIN customers c ON p.customerid = c.customerid
WHERE c.customernumber = '100'
GROUP BY i.invoiceid

如果每张发票有多个付款行,这将为您提供金额总和

答案 1 :(得分:6)

非常感谢您的回复!

Saggi Malachi,不幸的是,该查询会在有多笔付款的情况下汇总发票金额。假设有一笔价值18美元和12美元的39美元发票有两笔付款。因此,而不是结果看起来像:

1   39.00   9.00

你最终会得到:

1   78.00   48.00

Charles Bretana,在我的查询到最简单的查询的过程中,我(愚蠢地)省略了另一个表customerIvoices,它提供了客户和发票之间的链接。这可用于查看尚未付款的发票。

经过多次努力之后,我认为以下查询会返回我需要的内容:

SELECT DISTINCT i.invoiceid, i.amount, ISNULL(i.amount - p.amount, i.amount) AS amountdue
FROM invoices i
LEFT JOIN invoicepayments ip ON i.invoiceid = ip.invoiceid
LEFT JOIN customerinvoices ci ON i.invoiceid = ci.invoiceid
LEFT JOIN (
  SELECT invoiceid, SUM(p.amount) amount
  FROM invoicepayments ip 
  LEFT JOIN payments p ON ip.paymentid = p.paymentid
  GROUP BY ip.invoiceid
) p
ON p.invoiceid = ip.invoiceid
LEFT JOIN payments p2 ON ip.paymentid = p2.paymentid
LEFT JOIN customers c ON ci.customerid = c.customerid
WHERE c.customernumber='100'

你们会同意吗?

答案 2 :(得分:2)

首先,发票表中是否应该有CustomerId?实际上,您无法对尚未付款的发票执行此查询。如果发票上没有付款,则该发票甚至不会显示在查询的输出中,即使它是外部联接......

此外,当客户付款时,您如何知道要将其附加到哪个发票?如果唯一的方法是通过支付到达的存根上的InvoiceId,那么您(可能不恰当地)将发票与支付它们的客户相关联,而不是与订购它们的客户相关联...... (有时发票可以由订购服务的客户以外的其他人支付)

答案 3 :(得分:2)

对于那些希望从同一个表中获取各种聚合值的人,我有一个提示。

假设我有用户和表格的表格,用户可以获得积分。因此它们之间的连接是1:N(一个用户,多个点记录)。

现在在表'points'中我还存储了有关用户获得积分的信息(登录,点击横幅等)。我想列出按SUM(points)订购的所有用户,然后按SUM(points WHERE type = x)列出。也就是说,按用户拥有的所有点排序,然后按用户获得的点数进行特定操作(例如登录)。

SQL将是:

SELECT SUM(points.points) AS points_all, SUM(points.points * (points.type = 7)) AS points_login
FROM user
LEFT JOIN points ON user.id = points.user_id
GROUP BY user.id

这是美丽的SUM(points.points * (points.type = 7)),其中内括号的计算结果为0或1,因此将给定的点值乘以0或1,这取决于它是否等于我们想要的点的类型。

答案 4 :(得分:0)

我知道这很晚了,但确实可以回答您的原始问题。

/*Read the comments the same way that SQL runs the query
    1) FROM 
    2) GROUP 
    3) SELECT 
    4) My final notes at the bottom 
*/
SELECT 
        list.invoiceid
    ,   cust.customernumber 
    ,   MAX(list.inv_amount) AS invoice_amount/* we select the max because it will be the same for each payment to that invoice (presumably invoice amounts do not vary based on payment) */
    ,   MAX(list.inv_amount) - SUM(list.pay_amount)  AS [amount_due]
FROM 
Customers AS cust 
    INNER JOIN 
Payments  AS pay 
    ON 
        pay.customerid = cust.customerid
INNER JOIN  (   /* generate a list of payment_ids, their amounts, and the totals of the invoices they billed to*/
    SELECT 
            inpay.paymentid AS paymentid
        ,   inv.invoiceid AS invoiceid 
        ,   inv.amount  AS inv_amount 
        ,   pay.amount AS pay_amount 
    FROM 
    InvoicePayments AS inpay
        INNER JOIN 
    Invoices AS inv 
        ON  inv.invoiceid = inpay.invoiceid 
        INNER JOIN 
    Payments AS pay 
        ON pay.paymentid = inpay.paymentid
    )  AS list
ON 
    list.paymentid = pay.paymentid
    /* so at this point my result set would look like: 
    -- All my customers (crossed by) every paymentid they are associated to (I'll call this A)
    -- Every invoice payment and its association to: its own ammount, the total invoice ammount, its own paymentid (what I call list) 
    -- Filter out all records in A that do not have a paymentid matching in (list)
     -- we filter the result because there may be payments that did not go towards invoices!
 */
GROUP BY
    /* we want a record line for each customer and invoice ( or basically each invoice but i believe this makes more sense logically */ 
        cust.customernumber 
    ,   list.invoiceid 
/*
    -- we can improve this query by only hitting the Payments table once by moving it inside of our list subquery, 
    -- but this is what made sense to me when I was planning. 
    -- Hopefully it makes it clearer how the thought process works to leave it in there
    -- as several people have already pointed out, the data structure of the DB prevents us from looking at customers with invoices that have no payments towards them.
*/