2009-08-06 11 views
6

Odziedziczyłem następujący projekt DB. Tabele są:SQL JOIN, GROUP BY na trzech tabelach, aby uzyskać sumy

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

invoices 
-------- 
invoiceid 
amount 

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

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

Moje zapytanie musi powrócić invoiceid, kwota faktury (w tabeli faktur), a kwota do zapłaty (kwota faktury minus wszelkie płatności, które zostały wprowadzone do faktury) dla danego CUSTOMERNUMBER. Klient może mieć wiele faktur.

Poniższe zapytanie daje mi zduplikowane rekordy podczas wielokrotnego dokonywania płatności do faktury:

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' 

Jak mogę rozwiązać ten problem?

+0

ile wierszy płatności faktury może istnieć dla jednej faktury? ile płatności może istnieć dla każdej płatności? –

Odpowiedz

10

Nie jestem pewien, mam cię, ale to może być to, czego szukasz:

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 

Byłoby Ci kwot sum w przypadku istnieje wiele wierszy płatności dla każdej faktury

+1

Powinieneś prawdopodobnie owinąć kolumny 'amount' w' coalesce' lub 'isnull'; z MS-SQL, dowolne wartości NULL, np. z 'left join's spowoduje, że cała sprawa zostanie oceniona na NULL. Wiem, że niektóre dialekty SQL dopuszczają NULL == 0, ale to prowadzi do wojny religijnej, więc ... – Adrien

+0

Masz rację, dodałem klauzule "case", ponieważ nie jestem pewien, jakiego SQL używa. dzięki! –

+0

Osobiście nie SUMbym kwot faktur - zakładając, że faktura jest kluczem podstawowym i że kwota w tabeli faktury jest ograniczona, aby NIE była NULL - i zamiast tego dodaj i.amount do klauzuli GROUP BY. –

2

Pierwszy nie powinien istnieć CustomerId w tabeli Faktury? W tej chwili nie można wykonać tego zapytania dla faktur, które nie mają na razie żadnych płatności. Jeśli nie ma żadnych płatności na fakturze, faktura ta nie pojawi się nawet w wyniku zapytania, nawet jeśli jest zewnętrznym łącznikiem ...

Ponadto, gdy klient dokonuje płatności, skąd wiadomo, Faktura do której ma zostać dołączona? Jeśli jedyną drogą jest faktura na odcinku, który pojawia się wraz z płatnością, to (prawdopodobnie niewłaściwie) wiążesz faktury z klientem, który je zapłacił, a nie z klientem, który je zamówił .... (Czasami fakturę może zapłacić ktoś inny niż klient, który zamówił usługę).

+0

To są naprawdę dobre punkty; chociaż powiedziałbym, że twoje ostatnie oświadczenie jest regułą biznesową, która może, ale nie musi, być prawdziwa w firmie plakatu. Ogólnie rzecz biorąc, projekt jest bałaganiarski, a ponieważ nie ma sensu używać zewnętrznych połączeń, ponieważ predykat wymaga, aby wiersz był zwracany przez klientów –

4

Dziękuję bardzo za odpowiedzi!

Saggi Malachi, to zapytanie niestety sumuje kwotę faktury w przypadkach, gdy jest więcej niż jedna płatność. Załóżmy, że są dwie płatności do faktury za 39 USD w wysokości 18 USD i 12 USD. Więc zamiast kończąc z wynikiem, który wygląda tak:

1 39.00 9.00 

Skończysz z:

1 78.00 48.00 

Charles Bretana, w trakcie przycinania mojej kwerendy w dół do najprostszej możliwej zapytań I (głupio) pominięto dodatkową tabelę, faktury klienta, która zapewnia połączenie między klientami a fakturami. Można to wykorzystać do wyświetlenia faktur, za które nie dokonano płatności.

Po wielu walczy, myślę, że poniższa kwerenda zwraca co muszę go:

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' 

byś faceci zgodzić?

+0

tak, że wygląda dobrze. – RiddlerDev

2

Mam wskazówkę dla tych, którzy chcą uzyskać różne zagregowane wartości z tej samej tabeli.

Powiedzmy, że mam tabelę z użytkownikami i tabelą z punktami, które uzyskują użytkownicy. Zatem połączenie między nimi wynosi 1: N (jeden użytkownik, wiele rekordów punktów).

Teraz w tabeli "punkty" Przechowuję również informacje o tym, do czego użytkownik uzyskał punkty (login, kliknięcie bannera itp.).I chcę listę wszystkich użytkowników zamówionych przez SUM(points), a następnie przez SUM(points WHERE type = x). Oznacza to, że uporządkowane przez wszystkie punkty użytkownika, a następnie przez punkty, które użytkownik otrzymał dla określonej akcji (np. Login).

SQL to:

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 

Zaletą tej znajduje się w SUM(points.points * (points.type = 7)) gdzie wewnętrzna nawias ocenia się wartość 0 lub 1, a zatem mnożąc wartość danych punktów przez 0 lub 1, zależnie od wheteher jest równa do rodzaju punktów, które chcemy.