web-dev-qa-db-fra.com

Comment obtenir le nombre maximum de lignes par groupe/partition dans SQL Server?

J'utilise SQL Server 2005. J'ai une table de paiement avec les identifiants de paiement, les identifiants d'utilisateur et les horodatages. Je veux trouver le paiement le plus récent pour chaque utilisateur. C'est facile à rechercher et à trouver une réponse. Ce que je veux aussi savoir, c'est si le paiement le plus récent est le premier paiement de l'utilisateur ou non.

J'ai le suivant qui numérotera les paiements de chaque utilisateur:

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

Je ne fais pas le saut mental qui me permet ensuite de choisir le numéro de paiement le plus élevé par utilisateur. Si j'utilise ce qui précède comme sous-sélection en utilisant MAX (paymentNumber) puis en regroupant par user_id, je perds le payment_id dont j'ai besoin. Mais si j'ajoute également payment_id dans la clause group by, je reviens à une ligne par paiement. Je suis sûr que je néglige l'évidence. De l'aide?

11
DaveBurns

Essaye ça:

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       
27
Chandu

Refais la même chose.

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

Le paiement le plus récent porte maintenant reversePaymentNumber 1 et le nombre de paiements sera paymentNumber.

10
btilly
; 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
2
Ben Thul
SELECT * FROM (
        SELECT ROW_NUMBER() OVER(PARTITION BY OS.ContactId ORDER BY OS.Date ASC) AS FirstRow#,
        ROW_NUMBER() OVER(PARTITION BY OS.ContactId ORDER BY OS.Date DESC) AS LastRow#,
        OS.Contactid,CONVERT(VARCHAR,OS.Date,106) 'Purchase Month',
        OS.ProductId 'MyCII Subscription/Directory', OS.Charges 'Amount(INR)',OS.Date 'RAWDate'
        FROM tblOnlineServices OS
        WHERE Date IS NOT NULL AND Contactid IN('C000013112','C000010859') 
    ) FirstPurchase 
    WHERE FirstRow# = 1 OR LastRow# = 1
    ORDER BY Contactid, RAWDate
1
Abhishek Mukherjee

Que dis-tu de ça?

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
0
MarioVW

une façon moins cool, je suppose

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