tags:

views:

424

answers:

2

I have two tables Invoices and Payments. invoices have payments. I want to write a query that displays unpaid invoices and the remaining amount of the invoice, which is calculated by summing up the payments of the invoice and subtracting it from the invoice amount. I tried this query but it doesn't work. please how can i do it.

SELECT Invoice.[Invoice Date], Invoice.Item, Invoice.Quantity, 
    Invoice.[Unit Price], 
    Invoice.[Payment Status], Invoice.[LongDate], 
    Invoice.Quantity*Invoice.[Unit Price] - Sum(Q.Amount) AS Remaining
FROM  
    (SELECT Invoice.[Invoice Id], [Payment ID]
       FROM Invoice 
       INNER JOIN Payment ON Invoice.[Invoice Id] = Payment.[Invoice Id]) AS Q
    INNER JOIN Invoice ON Q.[Invoice Id] = Invoice.[Invoice Id]
GROUP BY Invoice.[Invoice Id]; 
+1  A: 
SELECT Invoice.[Invoice ID], 
    Sum(Invoice.Quantity * Invoice.[Unit Price]) 
    - COALESCE(Sum(Payment.Amount), 0) AS Remaining
FROM
    Invoice LEFT JOIN Payment ON Invoice.[Invoice ID] = Payment.[Invoice ID]
GROUP BY Invoice.[Invoice ID]

EDIT: I am assuming, you won't need Item related information in the result.
LEFT JOIN is used with an assumption that Invoice might not have a Payment record.

shahkalpesh
When testing this I get `Incorrect syntax near 'Invoice'`. Aren't you missing a comma between the fields in your select statement?
Mark Byers
Corrected. Thanks Mark. I wrote the query without testing it.
shahkalpesh
Now gives `Invalid column name 'InvoiceID'`, should be [Invoice ID], and I think you're missing a GROUP BY?
Mark Byers
@Mark: I think I should have tried the query with an example to see if it works. I think writing it straight into textbox on SO without checking is not the right way to answer.
shahkalpesh
Why a LEFT JOIN?
Jonathan Leffler
@Jonathan: Seems possible (and likely) that an invoice would have no payments at some point in time, so it would have to be a left join if you're searching for invoice that are *not* fully paid.
Aaronaught
@Aaronoaght: yes, you're probably right, but then you have to deal with the null 'Payment.Amount', perhaps with NVL or something similar.
Jonathan Leffler
@Jonathan: I have edited the answer with `COALESCE` to handle null values (i.e if there are no rows in Payment table).
shahkalpesh
@shahkalpesh: Have you considered how the OP can select the extra fields they need? Just adding them to this query will give an error (if it were MySQL it would work, but I'd guess that it is Sql Server).
Mark Byers
@Mark: I think OP wouldn't need it. Showing the remaining amount for each of the invoice item will give a wrong picture. If the OP wants to average the pending amount for each item, it will be a different SQL.
shahkalpesh
@shahkalpesh: Actaully I meant fields like Invoice.[Payment Status] and Invoice.[LongDate] that are in his original query.
Mark Byers
@Mark: Yes. But I am unable to understand why is OP storing records in this fashion? (i.e if the invoice could have more than 1 item)
shahkalpesh
+1  A: 

Try this:

SELECT
    Invoice.[Invoice Id],
    Invoice.Quantity * Invoice.[Unit Price] - COALESCE(Amount, 0) AS Remaining
FROM Invoice
LEFT JOIN (
    SELECT [Invoice Id], SUM(Amount) AS Amount
    FROM Payment
    GROUP BY [Invoice Id]
) T1
ON Invoice.[Invoice Id] = T1.[Invoice Id]

Of course you will also need to add the other columns to the select too, but I don't think they are relevant to this question so I omitted them for clarity.

Here is some test data I used to test this:

CREATE TABLE Invoice ([Invoice Id] INT NOT NULL, Quantity INT NOT NULL, [Unit Price] INT NOT NULL);
INSERT INTO Invoice ([Invoice Id], Quantity, [Unit Price]) VALUES
(1, 10, 5),
(2, 20, 10),
(3, 1, 1);

CREATE TABLE Payment ([Invoice Id] INT NOT NULL, Amount INT NOT NULL);
INSERT INTO Payment ([Invoice Id], Amount) VALUES
(1, 10),
(2, 100),
(2, 15);

And the result with this data:

Id  Remaining
1   40
2   85
3   1
Mark Byers
sorry for late reply, but my database is in access an i think COALESCE is not in access.
@user249641: I think you have to use `NZ` instead. Remember always to tag the type of database in the question.
Mark Byers