简体   繁体   中英

Using Top in SQL Server 2012

I have a table Contacts , parent to table Activity . I would like to select the latest activity for each contact, but getting more than one row.

This is my query:

select top 30 
     * 
from 
     Contacts o, Activity d  
where 
     o.ID = d.contact 
     and d.ID > 401061 
     and Last_Action is null 
order by 
     d.activity_date desc

I think I need Top? but not sure how to implement here. Any help would be appreciated.

You can use row_number() to number each contact's activities. In an outer query, you can filter down to only the latest activity per contact:

select  top 30 * 
from    (
        select  row_number() over (
                    partition by o.ID
                    order by d.activity_date desc) as rn
        ,       *
        from    Contacts o
        join    Activity d
        on      o.ID = d.contact 
        where   d.ID > 401061 
                and Last_Action is null 
        ) as SubQueryAlias
where   rn = 1 -- Only last activity per contact
order by 
         activity_date desc

Here's a way using not exists that will work on most dbs. You're basically selecting each activity per contact where a newer activity does not exist (therefore it's the latest activity).

select top 30 * from activity a
join contact c on c.id = a.contact
where not exists (
    select 1 from activity b
    where b.contact = a.contact
    and b.activity_date > a.activity_date
) and last_action is null and a.id > 401061
order by a.activity_date desc

This is slight guesswork as I can't see what your tables look like in terms of columns, but perhaps this (or something like it) would work?

select top 30 * from Contacts o, 
(SELECT contact, max(activity_date) FROM Activity GROUP BY contact) d  
    where o.ID = d.contact And d.ID > 401061 
    and Last_Action is null order by d.activity_date desc

I think you want to use a subquery:

SELECT TOP 30 *
FROM
  Contacts AS o,
  ( SELECT
      contact,
      MAX( activity_date ) AS activity_date
    FROM
      Activity
    WHERE
      contact > 401061 AND
      Last_Action IS NULL
    GROUP BY
      contact
  ) AS d
WHERE
  o.ID = d.contact
ORDER BY
  d.activity_date

Assuming you want the top 30 actions per contact, this is the exact kind of thing CROSS APPLY was invented for.

Something like the following - uncertainties are because I can't see an example of your data.

select
    *
from
    contacts
    cross apply (
        select top 30
            *
        from
            activity
        where
                contacts.id = activity.contact
                and 401061 < activity.id
    ) as _ca
where
    last_action is null -- Perhaps you could move this into the CA - but we don't know which table it's from
order by
    activity.activity_date desc;

edit

select top 30
    *
from
    contacts
    cross apply (
        select top 1
            *
        from
            activity
        where
            contacts.id = activity.contact
            and 401061 < activity.id
        order by
            activity.activity_date desc
    ) as _ca
where
    last_action is null -- assuming this is in table contacts
order by
    _ca.activity_date desc;    

Using a cte

WITH cte AS 
(
    SELECT *, 
           ROW_NUMBER() OVER (PARTITION BY contact ORDER BY activity_date DESC) rn
    FROM   Activity
    WHERE  d.ID > 401061  AND Last_Action IS NULL 
)
SELECT TOP 30 * 
FROM Contacts o 
     JOIN cte d  ON o.Id = d.contact 
WHERE d.rn = 1
ORDER BY cte.activity_date DESC

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM