2008-12-01 12 views
7

Digamos que tengo 2 tablas: clientes y pedidos. Un cliente puede tener muchos pedidos.Ayuda de instrucción SQL - Seleccione el último pedido para cada cliente

Ahora, tengo que mostrar a los Clientes con su último pedido. Esto significa que si un cliente tiene más de un pedido, muestre solo el pedido con el último horario de entrada.

Esto es lo lejos que he conseguido por mi cuenta:

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

Por supuesto, esto devuelve todos los clientes con una o más órdenes, mostrando la última orden primero para cada cliente, lo cual no es lo que quería. Mi mente estaba atascada en este punto, así que espero que alguien pueda señalarme en la dirección correcta.

Por alguna razón, I piensa Necesito usar la sintaxis MAX en alguna parte, pero me acaba de escapar en este momento.

ACTUALIZACIÓN: Después de pasar por algunas respuestas aquí (hay un montón!), Me di cuenta de que cometí un error: me refería cualquier cliente con su último disco. Eso significa que si él no tiene una Orden, entonces no necesito enumerarlo.

UPDATE2: Se arregló mi propia instrucción de SQL, que probablemente causó confusión sin fin a los demás.

+0

Su tabla de clientes en este ejemplo tiene una OrderId. ¿Está bien? –

+0

Sí, como está implícito en el SQL. – alextansc

+0

Si su tabla de clientes tiene un ID de pedido, su pregunta no tiene sentido. Si fuera cierto, diría que cada orden tiene un cliente diferente. ¿Estás seguro de que tu tabla de pedidos no tiene un ID de cliente? –

Respuesta

8

No creo que usted no desea utilizar (MAX) ya que no desea agrupar el OrderID. Lo que necesita es una consulta sub ordenada con un TOP SELECT 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

Sí, creo que su respuesta es la más correcta hasta el momento, el único error es que no ordenó la fecha en orden descendente. No mencionar el más elegante. :) ¡Gracias! – alextansc

+0

Hice el pedido al principio, luego me confundí y pensé que querías el pedido más antiguo. Ahora he actualizado mi ejemplo para que sea correcto. –

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

Está recibiendo el pedido con el Id. De pedido máximo y no el Tiempo de entrada máximo, que puede ser diferente. –

3

Algo así debe hacerlo:

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 

Esto supone que dos pedidos para el mismo cliente pueden tener el mismo EntryTime, por lo que MAX(OrderID) se utiliza en subconsulta Y para asegurar que sólo ocurre una vez por cliente Se utiliza el LEFT JOIN porque indicó que deseaba mostrar todos los clientes; si no han recibido ningún pedido, el último ID de pedido será NULL.

Espero que esto ayude!

-

ACTUALIZACIÓN :-) Esta muestra sólo los clientes con órdenes:

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

Esta solución es mucho más rápida que la que seleccionó el OP. –

0

algo como:

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

Aunque veo que ya ha aceptado una respuesta, creo que éste es un poco más intuitivo:

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 
         ); 

Tendría que ejecutar algo como esto a través de un plan de ejecución para ver la diferencia en la ejecución, pero cuando la función TOP se realiza después del hecho y que usar "ordenar por" puede ser costoso, creo que usar max (EntryTime) sería la mejor manera de ejecutar esto.

+0

He intentado su consulta, y la encuentro devolviendo el mismo resultado. ¡Bonito! No voy a cambiar mi respuesta en este momento, hay algunas otras respuestas aquí que quería probar. Pero, voy a votar su respuesta por el momento. :) – alextansc

+0

Seguramente esa cláusula sub query's where debería ser CustomerId = a.Id? –

1

Puede usar una función de ventana.

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 

Para cada cliente (a.id) ordena todos los pedidos y descarta todo menos lo último. La cláusula ORDER BY incluye la fecha de pedido y la identificación de entrada, en caso de que haya varios pedidos en la misma fecha.

En general, las funciones de ventana son mucho más rápidas que las búsquedas que usan MAX() en un gran número de registros.

0

Un enfoque que no he visto más arriba aún:

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 

Este (al igual que las otras soluciones creo) asume que no hay dos pedidos para el mismo cliente puede tener la misma hora exacta de entrada. Si eso es una preocupación, entonces tendría que hacer una elección en cuanto a qué determina cuál es el "último". Si eso es una preocupación publica un comentario y puedo expandir la consulta si es necesario para dar cuenta de eso.

El enfoque general de la consulta es encontrar el pedido para un cliente donde no hay otro pedido para el mismo cliente con una fecha posterior. Es entonces el último orden por definición. Este enfoque a menudo ofrece un mejor rendimiento que el uso de tablas o subconsultas derivadas.

+0

He probado esta consulta, y funciona, al igual que las otras dos respuestas hasta ahora. ¿Te importaría agregar cómo puedes manejar 2 pedidos con el mismo tiempo de entrada? – alextansc

+0

Interesante. ¿No se volvería esto exponencialmente más lento para cada pedido adicional que un cliente tenga, ya que la base de datos necesitará unir cada pedido a cada otro orden? –

0

Esta consulta es mucho más rápido que la respuesta aceptada:

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 
Cuestiones relacionadas