2009-08-06 19 views
6

Ho ereditato il seguente progetto di DB. Le tabelle sono:SQL JOIN, GROUP BY su tre tabelle per ottenere totali

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

invoices 
-------- 
invoiceid 
amount 

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

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

La mia domanda ha bisogno di tornare InvoiceID, l'importo della fattura (nella tabella fatture), e l'importo dovuto (importo della fattura al netto di eventuali pagamenti che sono stati fatti verso la fattura) per un dato CUSTOMERNUMBER. Un cliente può avere più fatture.

La seguente query mi dà i record duplicati quando i pagamenti sono fatti più di una fattura:

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' 

Come posso risolvere questo problema?

+0

quante righe di pagamento delle fatture possono esistere per una fattura? quanti pagamenti possono esistere per ogni paymentid? –

risposta

10

io non sono sicuro che ho ottenuto, ma questo potrebbe essere quello che stai cercando:

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 

Ciò porterà gli importi somme nel caso in cui non ci sono più righe di pagamento per ogni fattura

+1

Probabilmente dovresti racchiudere le colonne 'amount' in' coalesce' o 'isnull'; con MS-SQL, qualsiasi valore NULL, ad es. dal 'left join's l'intero oggetto verrà valutato su NULL. So che alcuni dialetti SQL consentono NULL == 0, ma questo porta a una guerra religiosa, quindi ... – Adrien

+0

Hai ragione, ho aggiunto le clausole "case" poiché non sono sicuro di cosa stia usando SQL dbms. Grazie! –

+0

Personalmente, non vorrei sommare gli importi delle fatture - assumendo che invoiceid sia una chiave primaria e che l'importo nella tabella delle fatture sia vincolato ad essere NOT NULL - e invece aggiungere i.amount alla clausola GROUP BY. –

2

First di tutto, non dovrebbe esserci un CustomerId nella tabella Fatture? Così com'è, non è possibile eseguire questa query per Fatture che non hanno ancora pagamenti su di loro. Se non ci sono pagamenti su una fattura, tale fattura non verrà nemmeno visualizzata nell'output della query, anche se è un join esterno ...

Inoltre, quando un cliente effettua un pagamento, come fai a sapere cosa Fattura per allegarlo a? Se l'unico modo è il InvoiceId sullo stub che arriva con il pagamento, allora sei (forse in modo inappropriato) associando le fatture al cliente che le ha pagate, piuttosto che al cliente che le ha ordinate .... (A volte una fattura può essere pagata da una persona diversa dal cliente che ha ordinato i servizi)

+0

Questi sono davvero buoni punti; anche se direi che la tua ultima affermazione è una regola aziendale che può o non può essere vera nella compagnia del poster. Nel complesso, il design è disordinato, e dato che non ha senso usare i join esterni, dal momento che il predicato richiede che venga restituita una riga dai Clienti –

4

Grazie mille per le risposte!

Saggi Malachi, la query purtroppo somma l'importo della fattura nei casi in cui vi è più di un pagamento. Diciamo che ci sono due pagamenti per una fattura $ 39 di $ 18 e $ 12. Quindi, piuttosto che finire con un risultato che assomiglia:

1 39.00 9.00 

Vi ritroverete con:

1 78.00 48.00 

Charles Bretana, nel corso della rifilatura mia domanda verso il basso per la più semplice domanda che possibile (stupidamente) ha omesso una tabella aggiuntiva, customerinvoices, che fornisce un collegamento tra i clienti e le fatture. Questo può essere usato per vedere fatture per le quali i pagamenti non sono stati effettuati.

Dopo molta lotta, penso che la seguente query restituisce quello che ho bisogno di:

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' 

sarebbe voi ragazzi concorrere?

+0

sì, sembra buona. – RiddlerDev

2

Ho un suggerimento per coloro che desiderano ottenere vari valori aggregati dalla stessa tabella.

Diciamo che ho tabella con utenti e tabella con punti acquisiti dagli utenti. Quindi la connessione tra loro è 1: N (un utente, molti record di punti).

Ora nella tabella "punti" memorizzo anche le informazioni relative a ciò che l'utente ha ottenuto i punti (login, clic su un banner ecc.).E voglio elencare tutti gli utenti ordinati per SUM(points) E quindi per SUM(points WHERE type = x). Vale a dire ordinato da tutti i punti che l'utente ha e quindi da punti che l'utente ha ottenuto per un'azione specifica (ad esempio login).

Lo SQL sarebbe:

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 

La bellezza di questo è nel SUM(points.points * (points.type = 7)) dove la parentesi interna restituisce 0 o 1 moltiplicando così il valore punti dati da 0 o 1, a seconda wheteher è uguale al tipo di punti che vogliamo.