views:

132

answers:

6

Hi,

I'm using Sql Server 2005 and i'm trying to achieve something like: In the same select statement i want to get the first x rows and the last x rows.

SELECT TOP(5) BOTTOM(5)

Of course 'bottom' does not exist so i need other solution. I believe there is an easy and elegant solution that i'm not getting. Doing the select again with Group By Desc is not an option.

Thanks.

+1  A: 

Check the link

SQL SERVER – How to Retrieve TOP and BOTTOM Rows Together using T-SQL

Did you try to using rownumber?

SELECT * 
FROM 
(SELECT *, ROW_NUMBER() OVER (Order BY columnName) as TopFive
   ,ROW_NUMBER() OVER (Order BY columnName Desc) as BottomFive
   FROM Table
)
WHERE TopFive <=5 or BottomFive <=5

http://www.sqlservercurry.com/2009/02/select-top-n-and-bottom-n-rows-using.html

hgulyan
this code will not work. `Top5` and `Bottom5` can't be referenced like this, you get `Msg 207, Level 16, State 1, Line 4 Invalid column name 'TopFive'...` If you put the `ROW_NUMBER...` in the WHERE you get `Msg 4108, Level 15, State 1, Line 1 Windowed functions can only appear in the SELECT or ORDER BY clauses.` You need to wrap the SELECT and FROM in a derived table and pull the WHERE to the outer query for this to work, like [@Paul did in the second method in his answer](http://stackoverflow.com/questions/2927475/sql-server-select-top-and-bottom-rows/2927515#2927515).
KM
Edited query. It was a big mistake.
hgulyan
This will run like a legless dog. Use UNION
gbn
A: 

Then you are out - doing the select again IS the only option, unless you want to pull in the complete result set and then throwing away everything in between.

ANY sql I cna think of is the same way - for the bottom you need to know first either how many items you have (materialize everything or use count(*)) or a reverse sort order.

Sorry if that does not suit you, but at the end.... reality does not care, and I do not see any other way to do that.

TomTom
+5  A: 

Using a union is the only thing I can think of to accomplish this

select * from (select top(5) * from logins order by USERNAME ASC) a
union
select * from (select top(5) * from logins order by USERNAME DESC) b
rdkleine
There's also a way of using rownumber. And you should right top 2.
hgulyan
@hgulyan: TOP (x) is the preferred method - it works with UPDATE and DELETE, too, and also allows you to write `TOP (20%)` or `TOP (@limit)` and so on
marc_s
Didn't know that though. Thanks for the information.
hgulyan
A: 

i guess you have to do it using subquery only

select * from table where id in ( 
           (SELECT id ORDER BY columnName LIMIT 5) OR 
           (SELECT id ORDER BY columnName DESC LIMIT 5) 
  )


select * from table where id in ( 
           (SELECT TOP(5) id ORDER BY columnName) OR 
           (SELECT TOP(5) id ORDER BY columnName DESC) 
  )

EDITED

select * from table where id in ( 
           (SELECT TOP 5 id ORDER BY columnName) OR 
           (SELECT TOP 5 id ORDER BY columnName DESC) 
  )
Salil
It's sql server, so instead of LIMIT, you need to use TOP
hgulyan
oohhhhhhhhh thanx @hgulyan i edit it may that helps :)
Salil
+2  A: 

Is it an option for you to use a union?

E.g.

select top 5 ... order by {specify columns asc} 
union 
select top 5 ... order by {specify columns desc}
Chris W
If you post code, *please* highlight the lines and click on the "code" button (101 010) on the editor toolbar!
marc_s
+1  A: 

I think you've two main options:

SELECT TOP 5 ...
FROM ...
ORDER BY ... ASC

UNION

SELECT TOP 5 ...
FROM ...
ORDER BY ... DESC

Or, if you know how many items there are in the table:

SELECT ...
FROM (
    SELECT ..., ROW_NUMBER() OVER (ORDER BY ... ASC) AS intRow
    FROM ...
) AS T
WHERE intRow BETWEEN 1 AND 5 OR intRow BETWEEN @Number - 5 AND @Number
Paul