2011-02-04 40 views
11

Estoy usando SQL Server 2005. Tengo una tabla de pagos con identificaciones de pago, identificaciones de usuario y marcas de tiempo. Quiero encontrar el pago más reciente para cada usuario. Esto es fácil de buscar y encontrar una respuesta. Lo que también quiero saber es si el pago más reciente es el primer pago del usuario o no.¿Cómo obtener el número máximo de filas por grupo/partición en SQL Server?

Tengo el siguiente que numerar los pagos de cada usuario:

SELECT 
    p.payment_id, 
    p.user_id, 
    ROW_NUMBER() OVER (PARTITION BY p.user_id ORDER BY p.payment_date) AS paymentNumber 
FROM 
    payment p 

No estoy haciendo el salto mental que luego me deja a continuación, elegir la más alta paymentNumber por usuario. Si uso lo anterior como una subselección usando MAX (paymentNumber) y luego agrupando por user_id, pierdo el payment_id que necesito. Pero si también agrego el payment_id en la cláusula group by, vuelvo a una fila por pago. Estoy seguro de que estoy pasando por alto lo obvio. ¿Alguna ayuda?

+0

hay una clave principal en la tabla en absoluto? – DForck42

+0

La clave principal es payment_id. – DaveBurns

Respuesta

25

Prueba esto:

SELECT a.*, CASE WHEN totalPayments>1 THEN 'NO' ELSE 'YES' END IsFirstPayment 
    FROM(
       SELECT p.payment_id,  
           p.user_id,  
           ROW_NUMBER() OVER (PARTITION BY p.user_id ORDER BY p.payment_date DESC) AS paymentNumber, 
           SUM(1) OVER (PARTITION BY p.user_id) AS totalPayments 
        FROM payment p 
      ) a 
WHERE paymentNumber = 1  
+1

¿Por qué usar SUM (1) cuando hay 'COUNT (*)' ?? – RichardTheKiwi

+0

¿Por qué no? ¿Hay algún problema de rendimiento? – Chandu

+1

'Contar (*)' sería más rápido, sensación instintiva. Ni siquiera necesita inspeccionar los registros, solo la longitud del índice. Suma (1) tiene que compensar el valor 1 para cada fila y contarlo. Normalmente no escribirías 'seleccionar suma (1) de tbl' solo para contar registros, ¿o sí? – RichardTheKiwi

2
; with cte as (
SELECT 
    p.payment_id, 
    p.user_id, 
    ROW_NUMBER() OVER (PARTITION BY p.user_id ORDER BY p.payment_date desc) AS paymentNumber 
FROM 
    payment p 
) select * from cte where paymentNumber = 1 
10

hacer lo mismo otra vez.

SELECT 
    p.payment_id, 
    p.user_id, 
    ROW_NUMBER() OVER (PARTITION BY p.user_id ORDER BY p.payment_date) AS paymentNumber, 
    ROW_NUMBER() OVER (PARTITION BY p.user_id ORDER BY p.payment_date DESC) AS reversePaymentNumber, 
FROM 
    payment p 

Ahora el pago más reciente ha reversePaymentNumber 1, y el número de pagos se paymentNumber.

+1

Su respuesta me ayudó a seleccionar grupos de filas con más de 1 fila en la partición. Sume el número de fila y el número de fila inversa y las filas seleccionadas donde la suma fue mayor que 2. ¡Gracias! –

0

¿Qué tal esto?

SELECT 
    p.user_id, 
    MAX(p.payment_date) as lastPayment, 
    CASE COUNT(p.payment_id) WHEN 1 THEN 1 ELSE 0 END as isFirstPayment 
FROM 
    payment p 
GROUP BY 
    p.user_id 
+1

SQL Server no tiene tipos de datos booleanos 'COUNT (p.payment_id) = 1' tendría que ser una expresión' case '. –

+0

Además, necesito el payment_id como salida. Como dije en la pregunta original, si pones eso en la cláusula output y group by, entonces obtienes una fila por cada pago, no solo el último. – DaveBurns

0

una manera menos frío supongo

; with maxp as 
(
    select 
     p.user_id, 
     max(p.payment_date) as MaxPaymentDate 
    from payment p 
    group by p.userid 
), 
nump as 
(
    select 
     p.payment_id,  
     p.user_id,  
     p.payment_date, 
     ROW_NUMBER() OVER (PARTITION BY p.user_id ORDER BY p.payment_date) AS paymentNumber 
    FROM payment p 
), 
a as 
(
select 
    nump.payment_id, 
    nump.user_id, 
    nump.paymentNumber 
    case when maxp.MaxPaymentDate is null then 'Old' else 'New' end as NewState 
from nump 
    left outer join maxp 
     on nump.user_id=maxp.user_id 
      and nump.payment_date=maxp.MaxPaymentDate 
) 

select 
* 
from a 
where NewState='New' 
Cuestiones relacionadas