2011-02-04 1 views
11

Sto utilizzando SQL Server 2005. Dispongo di una tabella di pagamenti con ID di pagamento, ID utente e data e ora. Voglio trovare il pagamento più recente per ciascun utente. È facile cercare e trovare una risposta. Quello che voglio anche sapere è se il pagamento più recente è il primo pagamento dell'utente o meno.Come ottenere il numero massimo di righe per gruppo/partizione in SQL Server?

ho il seguente che numerare i pagamenti di ciascun utente:

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 

Non sto facendo il salto mentale che poi mi permette poi a scegliere il più alto paymentNumber per utente. Se utilizzo il suddetto come sottoselezione usando MAX (paymentNumber) e poi raggruppando per user_id, perdo il payment_id di cui ho bisogno. Ma se aggiungo anche il payment_id al gruppo per clausola, torno a una riga per pagamento. Sono sicuro che sto trascurando l'ovvio. Qualsiasi aiuto?

+0

c'è una chiave primaria nella tabella a tutti? – DForck42

+0

La chiave primaria è payment_id. – DaveBurns

risposta

25

Prova questa:

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

Perché usare SUM (1) quando c'è 'COUNT (*)' ?? – RichardTheKiwi

+0

Perché no? C'è qualche problema di prestazioni? – Chandu

+1

'Conteggio (*)' sarebbe più veloce, sensazione di budello. Non ha nemmeno bisogno di ispezionare i record, basta indicizzare la lunghezza. Sum (1) deve recuperare il valore 1 per ogni riga e totalizzarlo. Normalmente non scriveresti 'select sum (1) da tbl' solo per contare i record, vero? – 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

fare di nuovo la stessa cosa.

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 

Ora il pagamento più recente ha reversePaymentNumber 1, e il numero di pagamenti sarà paymentNumber.

+1

La tua risposta mi ha aiutato a selezionare gruppi di righe con più di 1 riga nella partizione. Ho riassunto il numero di riga e il numero di riga inversa e le righe selezionate dove la somma era maggiore di 2. Grazie! –

0

Che ne dici di questo?

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 non ha tipi di dati booleani 'COUNT (p.payment_id) = 1' dovrebbe essere un'espressione' case'. –

+0

Inoltre, ho bisogno di payment_id come output. Come ho detto nella domanda originale, se lo metti nell'output e nella clausola group by, ottieni una riga per ogni pagamento, non solo l'ultima. – DaveBurns

0

un modo meno freddo suppongo

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