简体   繁体   中英

How to get matching data using rownumber in SQL Server?

I am new to SQL Server and I don't know how to word this question. I have feeling this might be repeated. If you know it please flag it as duplicate. I will explain with data what I am trying to achieve

Table data - sometable

ID  TKID    Status    DateTimeStamp            RunMin
-----------------------------------------------------
215  6      Start   2009-10-29 09:48:14.243    NULL
261  6      Stop    2009-10-30 10:05:16.460    1457
356  6      Start   2009-11-03 14:11:05.097    NULL
357  6      Stop    2009-11-03 15:20:05.133    1526
358  6      Start   2009-11-03 16:14:45.863    NULL
574  19     Start   2009-11-12 13:12:11.827    NULL
575  19     Stop    2009-11-12 13:47:23.077    35
543 259     Start   2009-11-12 09:01:24.013    NULL 
620 259     Stop    2009-11-14 11:25:30.177    NULL 
623 259     Start   2009-11-14 16:47:32.913    NULL 
720 352     Start   2009-11-18 17:47:38.637    NULL 
730 352     Stop    2009-11-19 08:22:28.317    874  
773 352     Start   2009-11-20 10:00:11.320    NULL 
778 352     Stop    2009-11-20 11:51:59.853    985  
812 352     Start   2009-11-20 17:51:35.640    NULL 
813 352     Stop    2009-11-20 17:53:52.373    987  
822 352     Start   2009-11-23 08:13:23.030    NULL 
823 352     Stop    2009-11-23 08:17:33.063    991  
901 352     Start   2009-12-01 10:50:16.547    NULL 
910 352     Stop    2009-12-01 10:50:54.200    991  

Expected output:

ID  TKID    Status    DateTimeStamp            RunMin      
-----------------------------------------------------
358  6      Start   2009-11-03 16:14:45.863    NULL     
623 259     Start   2009-11-14 16:47:32.913    NULL 

So basically I want to get the record which has the start status but doesn't have the stop status.

Now What I tried ..

I tried to use the ROW_NUMBER function as below,

;with cte as 
(
    select 
       *,
       ROW_NUMBER() OVER (PARTITION BY tkid 
                          ORDER BY tkid, DateTimeStamp) AS rn
    from Prog_Timer
)
SELECT * 
FROM 
   (SELECT * 
    FROM cte
    WHERE TkID IN (SELECT TkID
                   FROM cte 
                   GROUP BY TkID
                   HAVING COUNT(*)% 2 = 1)
) as d

It gives me result as

And then try to get the tkid with the Odd count and for that tkid got the data.

ID  TKID    Status    DateTimeStamp            RunMin
-----------------------------------------------------
215  6      Start   2009-10-29 09:48:14.243    NULL
261  6      Stop    2009-10-30 10:05:16.460    1457
356  6      Start   2009-11-03 14:11:05.097    NULL
357  6      Stop    2009-11-03 15:20:05.133    1526
358  6      Start   2009-11-03 16:14:45.863    NULL
543 259     Start   2009-11-12 09:01:24.013    NULL 
620 259     Stop    2009-11-14 11:25:30.177    NULL 
623 259     Start   2009-11-14 16:47:32.913    NULL 

I don't know how to get the last row only from this output for each tkid select only the last start row. I think my approch is pretty complicated. There has to be simple way to get what I want. If you have new approach feel free to post. If you have anything to add to my existing query feel free to post. In case of confusion feel free to comment.

If you modify your ROW_NUMBER() PARTITION to order by DateTimeStamp desc , then the latest row for each tkid will be given rn=1 , thus giving you the latest status for each tkid . You then simply have to SELECT rows where the rn = 1 and the Status = 'Start' to get your desired output:

select * from
(select 
   *,
   ROW_NUMBER() OVER (PARTITION BY tkid order by DateTimeStamp desc) as rn
from Prog_Timer
)
T
where 
T.rn = 1 -- the latest status for each tkid 
and T.Status = 'Start' -- returns only started and not stopped timers
-- if timer is stopped, t.Status will be 'Stop' in the latest row

Lot of ways to do this, one of them is below:

SELECT T.*
FROM SOMETABLE T
JOIN (SELECT MAX(DATETIMESTAMP) MAXTIME, TKID
    FROM SOMETABLE 
    GROUP BY TKID) SRC
        ON SRC.MAXTIME = T.DATETIMESTAMP AND SRC.TKID = T.TKID
WHERE T.STATUS = 'Start'

Depending on the actual data, you might want to get MAX(ID) instead, in case it's sometimes possible for a process to stop and start at the same time. But of course, in case of updates it could be possible for older ID values to have newer timestamps.

I think this can show your results:

select *
from Prog_Timer st
where st.[Status] = 'Start' And
    isnull((select min(sti.DateTimeStamp) from Prog_Timer sti  
            where sti.[Status] = 'Stop' And sti.TKId = st.TKId And sti.DateTimeStamp > st.DateTimeStamp )
        , cast('2999/12/29' as datetime)) 
    > 
    isnull((select min(stii.LogDateTime) from Prog_Timer stii 
            where stii.[Status] = 'Start' And stii.TKId = st.TKId And stii.DateTimeStamp > st.DateTimeStamp)
        , cast('2999/12/29' as datetime))

But I'm waiting for best answer ;).

how about this:

;with cte as 
(
    select *
         , MAX(DateTimeStamp) OVER (PARTITION BY tkid) AS maxDt
    from Prog_Timer
)
SELECT * 
FROM cte
WHERE DateTimeStamp=maxDt and Status='Start'

Try this query:

SELECT * 
FROM tableName 
WHERE Status = 'start' 
  AND ID NOT IN (SELECT ID 
                 FROM tableName 
                 WHERE Status = 'stop')

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