简体   繁体   中英

How to get a negative rownumber in sql server

I have a set of data with a DateTime , say CalculatedOn what I would like is to get start at the current date getdate() and get an x amount of records from before the current date, and the same amount from after.

If x = 50 then 50 prior to now and 50 in front of now. I was thinking rownumber() would be perfect for this, however I cannot think of how to number the rows negative for prior and positive for future.

Also there is the issue of if there are not 50 prior or future what will happen, but that will come after.

Assume the table has just two columns :

create table MyTable
(
   Id int not null constraint pk_mytable primary key,
   SomeTextIWant nvarchar(50) not null,
   CalculateDate DateTime not null
);

Results :

If today is 25/04 12:54

then

Id, SomeTextIWant, CalculatedDate
-- 50 from before now--
-----now here-----
-- 50 from after now--

If you want to get 50 rows before and after, perhaps this will do what you want:

with cte1 as (
      select top 50 t.*
      from table t
      where CalculatedDate <= getdate()
      order by CalculatedDate desc
     ),
     cte2 as (
      select top 50 t.*
      from table t
      where CalculatedDate > getdate()
      order by CalculatedDate
     )
select *
from (select * from cte1 union all select * from cte2) t

EDIT:

It is not clear to me from the context of the question whether a row number is actually needed. It is easy enough to add, thoug:

(select top 50 t.*,
        - row_number() over (order by CalculatedDate desc) as rownumber
 from table t
 where CalculatedDate <= getdate()
 order by CalculatedDate desc
)
union all
(select top 50 t.*,
        row_number() over (order by CalculatedDate) as rownumber
 from table t
 where CalculatedDate > getdate()
 order by CalculatedDate
)

You can actually combine these into one query:

select t.*,
       ((case when CalculatedDate < getdate() then -1 else 1 end) *
        (row_number() over (partition by (case when CalculatedDate < getdate() then 1 else 0 end)
                           order by (case when CalculatedDate < getdate() then CalculatedDate end) desc, 
                                     CalculatedDate asc
                           )
         )) as rn
from table t;

You can put this in a subquery and select where rn between -50 and 50.

However, I'm not sure what to do about row number 0 and the question provides no information on what to do with any records that match getdate() (as unlikely as that is). I think the first answer does what that OP needs.

You can use two CTE's, one for past and one for future dates, then use ROW_NUMBER with ASC and DESC , multiply before now with -1 and concat all:

WITH dataBefore AS
(
    SELECT d.*, rn = (-1) * row_Number() over (Order By CalculatedOn DESC)
    FROM dbo.TableName d
    WHERE CalculatedOn < GetDate()
)
, dataAfter AS
(
    SELECT d.*, rn = row_Number() over (Order By CalculatedOn ASC)
    FROM dbo.TableName d
    WHERE CalculatedOn >= GetDate()
)
SELECT * FROM
(
    SELECT db.*
    FROM dataBefore db
    UNION ALL 
    SELECT da.*
    FROM dataAfter da
)x
WHERE x.rn >= -50 AND x.RN <= 50
ORDER BY x.RN

try this...

With myCte
As
(
    Select top 2 column1,column2 from  YourTable where yourdate > '2014-04-23'
    union 
    Select top 2 column1,column2 from  YourTable where yourdate  < '2014-04-23'
) select ROW_NUMBER() over (order by column1) as RNO,* from myCte

With RowNumber

SELECT TOP 50 ROW_NUMBER() OVER (ORDER BY CalculateDate) AS RowNum,
    id, SomeTextIWant, CalculateDate
FROM MyTable
WHERE CalculateDate > @Date

UNION ALL

SELECT TOP 50 -ROW_NUMBER() OVER (ORDER BY CalculateDate DESC) AS RowNum,
    id, SomeTextIWant, CalculateDate
FROM MyTable
WHERE CalculateDate < @Date

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