2008-12-01 11 views
7

Powiedz, że mam 2 tabele: Klienci i zamówienia. Klient może mieć wiele Zamówień.SQL Statement Pomoc - wybierz ostatnie zamówienie dla każdego klienta.

Teraz muszę pokazać każdemu klientowi ostatnie zamówienie. Oznacza to, że jeśli Klient ma więcej niż jedno Zamówienie, wyświetla tylko Zamówienie z najnowszym czasem wejścia.

Jest to, jak daleko udało mi się na własną rękę:

SELECT a.*, b.Id 
FROM Customer a INNER JOIN Order b ON b.CustomerID = a.Id 
ORDER BY b.EntryTime DESC 

To oczywiście zwraca wszystkie Klienci z jednego lub więcej zamówień, pokazując najnowsze Order pierwszy dla każdego Klienta, który nie jest to, co chciałem. W tym momencie mój umysł utknął w rytmie, więc mam nadzieję, że ktoś wskaże mi właściwy kierunek.

Z jakiegoś powodu, I myślę, że Potrzebuję użyć gdzieś składni MAX, ale to właśnie mi ucieka właśnie teraz.

UPDATE: Po przejściu kilku odpowiedzi tutaj (jest dużo!), Zdałem sobie sprawę, że popełniłem błąd: Chciałem żadnego Klientowi swojej najnowszej płycie. Oznacza to, że jeśli nie ma Orderu, nie muszę go wymieniać.

UPDATE2: Naprawiono własne polecenie SQL, które prawdopodobnie nie spowodowało końca zamieszania wśród innych.

+0

Twój stół klienta w tym przykładzie posiada idZamówienia. Czy to prawda? –

+0

Tak, jak sugeruje SQL. – alextansc

+0

Jeśli tabela klienta ma identyfikator zamówienia, pytanie nie ma sensu. Gdyby to była prawda, powiedziałbyś, że każde zamówienie ma innego klienta. Czy na pewno Twoja tabela zamówień nie ma identyfikatora klienta? –

Odpowiedz

8

Nie sądzę ty chcesz używać max (), ponieważ nie chcesz grupować identyfikatora zamówienia. Co trzeba to nakazał kwerendy sub z SELECT TOP 1.

select * 
from Customers inner join Orders 
on Customers.CustomerID = Orders.CustomerID 
and OrderID = (SELECT TOP 1 subOrders.OrderID 
        FROM Orders subOrders 
        WHERE subOrders.CustomerID = Orders.CustomerID 
        ORDER BY subOrders.OrderDate DESC) 
+0

Tak, myślę, że twoja odpowiedź jest jak dotąd najbardziej poprawna, jedynym błędem jest to, że nie posortowałeś daty w porządku malejącym. Nie wspomnieć o najbardziej eleganckim. :) Dzięki! – alextansc

+0

Zrobiłem najpierw zamówienie, potem pomyliłem się i myślałem, że chcesz najstarszego zamówienia. Zaktualizowałem mój przykład, by było poprawne. –

0
SELECT Cust.*, Ord.* 
FROM Customers cust INNER JOIN Orders ord ON cust.ID = ord.CustID 
WHERE ord.OrderID = 
    (SELECT MAX(OrderID) FROM Orders WHERE Orders.CustID = cust.ID) 
+0

Otrzymujesz zamówienie z Max OrderID, a nie Max EntryTime, które mogą się różnić. –

3

Coś jak to powinno zrobić:

SELECT X.*, Y.LatestOrderId 
FROM Customer X 
LEFT JOIN (
    SELECT A.Customer, MAX(A.OrderID) LatestOrderId 
    FROM Order A 
    JOIN (
    SELECT Customer, MAX(EntryTime) MaxEntryTime FROM Order GROUP BY Customer 
) B ON A.Customer = B.Customer AND A.EntryTime = B.MaxEntryTime 
    GROUP BY Customer 
) Y ON X.Customer = Y.Customer 

ta zakłada, że ​​dwa rzędy dla tego samego klienta mogą mieć taką samą EntryTime, dlatego MAX(OrderID) jest stosowany w podzapytanie Y aby upewnić się, że tylko występuje raz na klienta. Numer LEFT JOIN jest używany, ponieważ podałeś, że chcesz pokazać wszystkich klientów - jeśli nie mają żadnych zamówień, wówczas LatestOrderId będzie NULL.

Mam nadzieję, że to pomoże!

-

UPDATE :-) To pokazuje tylko klientów z zamówieniami:

SELECT A.Customer, MAX(A.OrderID) LatestOrderId 
FROM Order A 
JOIN (
    SELECT Customer, MAX(EntryTime) MaxEntryTime FROM Order GROUP BY Customer 
) B ON A.Customer = B.Customer AND A.EntryTime = B.MaxEntryTime 
GROUP BY Customer 
+0

To rozwiązanie jest znacznie szybsze niż to, które zostało wybrane przez OP. –

0

coś takiego:

SELECT 
    a.* 
FROM 
    Customer a 
    INNER JOIN Order b 
     ON a.OrderID = b.Id 
     INNER JOIN (SELECT Id, max(EntryTime) as EntryTime FROM Order b GROUP BY Id) met 
      ON 
      b.EntryTime = met.EntryTime and b.Id = met.Id 
6

Choć widzę, że masz już zaakceptowane odpowiedź, myślę, że ten jest nieco bardziej intuicyjny:

select  a.* 
      ,b.Id 

from  customer a 

inner join Order b 
on   b.CustomerID = a.Id 

where  b.EntryTime = (select max(EntryTime) 
          from Order 
          where Id = b.Id 
         ); 

Musiałbym uruchomić coś takiego poprzez plan wykonania, aby zobaczyć różnicę w wykonaniu, ale gdzie funkcja TOP jest wykonywana po fakcie i że używanie "order by" może być kosztowne, uważam, że używając max (EntryTime) byłby najlepszy sposób, aby to uruchomić.

+0

Wypróbowałem Twoje zapytanie i stwierdziłem, że zwraca ten sam wynik. Miły! W tej chwili jednak nie zmienię odpowiedzi, jest jeszcze kilka innych odpowiedzi, które chciałem przetestować. Ale na razie zagłosuję za twoją odpowiedzią. :) – alextansc

+0

Z pewnością ta klauzula, gdzie klauzula powinna być CustomerId = a.Id? –

1

Możesz użyć funkcji okna.

SELECT * 
    FROM (SELECT a.*, b.*, 
       ROW_NUMBER() OVER (PARTITION BY a.ID ORDER BY b.orderdate DESC, 
       b.ID DESC) rn 
      FROM customer a, ORDER b 
     WHERE a.ID = b.custid) 
WHERE rn = 1 

Dla każdego klienta (a.id) sortuje wszystkie zamówienia i odrzuca wszystko oprócz najnowszego. Klauzula ORDER BY zawiera zarówno datę zamówienia, jak i identyfikator pozycji, w przypadku gdy istnieje wiele zamówień tego samego dnia.

Zasadniczo funkcje okna są znacznie szybsze niż jakiekolwiek wyszukiwania przy użyciu MAX() na dużej liczbie rekordów.

0

Jedno podejście, że nie widziałem jeszcze wyżej:

SELECT 
    C.*, 
    O1.ID 
FROM 
    dbo.Customers C 
INNER JOIN dbo.Orders O1 ON 
    O1.CustomerID = C.ID 
LEFT OUTER JOIN dbo.Orders O2 ON 
    O2.CustomerID = C.ID AND 
    O2.EntryTime > O1.EntryTime 
WHERE 
    O2.ID IS NULL 

to (jak również inne rozwiązania wierzę) zakłada, że ​​nie ma dwóch zleceń dla tego samego klienta mogą mieć dokładnie ten sam czas wejścia. Jeśli jest to problemem, musisz dokonać wyboru, który z nich określa, który z nich jest "najnowszy". Jeśli to jest problem, opublikuj komentarz i jeśli to konieczne, rozwiążę zapytanie.

Ogólne podejście do zapytania polega na znalezieniu zamówienia dla klienta, w przypadku gdy nie ma innego zamówienia dla tego samego klienta z późniejszym terminem. Jest to najnowszy porządek z definicji. Takie podejście często zapewnia lepszą wydajność niż użycie wyprowadzonych tabel lub podkwerend.

+0

Przetestowałem to zapytanie i działa, tak jak pozostałe dwie dotychczasowe odpowiedzi. Czy mógłbyś dodać, jak możesz obsługiwać 2 zamówienia z dokładnie tym samym czasem wejścia? – alextansc

+0

Interesujące. Czy nie byłoby to wykładniczo wolniejsze dla każdego dodatkowego zamówienia, które klient ma, ponieważ baza danych będzie musiała dołączyć do każdego zamówienia do każdego innego zamówienia? –

0

To zapytanie jest znacznie szybsze niż zaakceptowanej odpowiedzi:

SELECT c.id as customer_id, 
    (SELECT co.id FROM customer_order co WHERE 
    co.customer_id=c.id 
    ORDER BY some_date_column DESC limit 1) as last_order_id 
    FROM customer c