Select top and bottom rows - sql

I'm using SQL Server 2005 and I'm trying to achieve something like this:
I want to get the first x rows and the last x rows in the same select statement.
SELECT TOP(5) BOTTOM(5)
Of course BOTTOM does not exist, so I need another 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.

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

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

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

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}

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)
)

No real difference between this and the union that I'm aware of, but technically it is a single query.
select t.*
from table t
where t.id in (select top 5 t2.id from table t2 order by MyColumn)
or
t.id in (select top 5 t2.id from table t2 order by MyColumn desc);

SELECT *
FROM (
SELECT x, rank() over (order by x asc) as rown
FROM table
) temp
where temp.rown = 1
or temp.rown = (select count(x) from table)

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.

I had to do this recently for a very large stored procedure; if your query is quite large, and you want to minimize the amount of queries you could declare a #tempTable, insert into that #tempTable then query from that #tempTable,
DECLARE #tempTable TABLE ( columns.. )
INSERT INTO #tempTable
VALUES ( SELECT.. your query here ..)
SELECT TOP(5) columns FROM #tempTable ORDER BY column ASC -- returns first to last
SELECT TOP(5) columns FROM #tempTable ORDER BY column DESC -- returns last to first

Related

How do I sample the number of records in another table?

I have code where I'm sampling 50,000 random records. I.e.,
SELECT * FROM Table1
SAMPLE 50000;
That works. However, what I really want to do is sample the number of records that are in a different table. I.e.,
SELECT * FROM Table1
SAMPLE count(*) FROM Table2;
I get an error. What am I doing wrong?
This is not randomized like sample, so bear that in mind. But there also won't be an obvious pattern, I believe it's determined by disk location (don't quote me on that).
SELECT *
FROM Table1
QUALIFY ROW_NUMBER() OVER
( PARTITION BY 1
ORDER BY 1
) <=
( SELECT COUNT(*)
FROM Table2
);
Better way
SELECT TMP.* -- Or list the columns you want with "rnd"
FROM ( SELECT RANDOM(-10000000,10000000) rnd,
T1.*
FROM Table1 T1
) TMP
QUALIFY ROW_NUMBER() OVER
( ORDER BY rnd
) <=
( SELECT COUNT(*)
FROM Table2
);
SELECT TOP 50000 * FROM Table1 ORDER BY NEWID()

SQL Server : how can I descend using an order top

I am trying to order the last 5 entries to the database and select them to show but I am having no luck, http://sqlfiddle.com/#!6/8bd8a/2 as you can see I replicated the code I have and you can see it is ordering by [id] how can I change the SQL code so that the 9 in the ID column starts at the bottom instead of at the top.. so that it can be like
5
6
7
8
9
One way is to wrap your original statement in an outer statement that sorts ascending:
select * from (
select top 5 *
from yourtable
order by id desc
) a
order by id asc
Sample SQL Fiddle
Or you could use a common table expression (cte):
WITH cte AS (
SELECT TOP (5) *
FROM YourTable ORDER BY id DESC
)
SELECT * FROM cte ORDER BY id ASC
The following query should provide you the expected result:
SELECT *
FROM (SELECT TOP 5 T2.*
FROM yourtable T2
ORDER BY T2.id DESC) T
ORDER BY T.id
Hope this will help you.

Can you select the Nth row in the dataset returned by SELECT statement?

In pseudo-code, what I mean is, can you do something like this (similar to TOP):
SELECT Row 2
*
FROM Table
WHERE Column1 = Condition
I wanted to do something for testing - I can't think of a real world scenario for this. Just curious if it's possible. I can't find anything on the Interwebz about it.
This would give you the 9th row:
select top 1 *
from (
select top 9 *
from MyTable
order by 1) as x
order by 1 desc
SELECT *
FROM (
SELECT *, ROW_NUMBER() OVER (ORDER BY col2) rn
FROM mytable
WHERE col1 = condition
) q
WHERE rn = 4
WITH QUERY AS (
SELECT *, ROW_NUMBER() OVER(ORDER BY Column1) AS RowNumber
FROM Table
)
SELECT * FROM QUERY WHERE ROWNUMBER = N

How can I get the n-th row in the Query results?

How can I get the n-th row of a TSQL query results?
Let's say, I want to get the 2nd row of this SELECT:
SELECT * FROM table
ORDER BY 2 ASC
What version of SQL Server are you targeting? If 2005 or greater, you can use ROW_NUMBER to generate a row number and select using that number. http://msdn.microsoft.com/en-us/library/ms186734.aspx
WITH orderedtable AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY <your order here>) AS 'RowNumber'
FROM table
)
SELECT *
FROM orderedtable
WHERE RowNumber = 2;
You can use a trick combining TOP with ORDER BY ASC/DESC to achieve an effect similar to MySQL's LIMIT:
SELECT TOP 2 * INTO #temptable FROM table
ORDER BY 2 ASC
SELECT TOP 1 * FROM #temptable
ORDER BY 2 DESC
or without temptable, but nested statements:
SELECT TOP 1 * FROM
(
SELECT TOP 2 * FROM table
ORDER BY 2 ASC
) sub
ORDER BY 2 DESC
The first time you select all rows up to the one you want to actually have, and in the second query you select only the first of the remaining when ordering them reversely, which is exactly the one you want.
Source: http://www.planet-source-code.com/vb/scripts/ShowCode.asp?txtCodeId=850&lngWId=5
One way;
;with T(rownumber, col1, colN) as (
select
row_number() over (order by ACOLUMN) as rownumber,
col1,
colN
from
atable
)
select * from T where rownumber = 2

SQL Server Top 1

In Microsoft SQL Server 2005 or above, I would like to get the first row, and if there is no matching row, then return a row with default values.
SELECT TOP 1 ID,Name
FROM TableName
UNION ALL
SELECT 0,''
ORDER BY ID DESC
This works, except that it returns two rows if there is data in the table, and 1 row if not.
I'd like it to always return 1 row.
I think it has something to do with EXISTS, but I'm not sure.
It would be something like:
SELECT TOP 1 * FROM Contact
WHERE EXISTS(select * from contact)
But if not EXISTS, then SELECT 0,''
What happens when the table is very full and you might want to specify which row of your top 1 to get, such as the first name? OMG Ponies' query will return the wrong answer in that case if you just change the ORDER BY clause. His query also costs about 8% more CPU than this modification (though it has equal reads)
SELECT TOP 1 *
FROM (
SELECT TOP 1 ID,Name
FROM TableName
ORDER BY Name
UNION ALL
SELECT 0,''
) X
ORDER BY ID DESC
The difference is that the inner query has a TOP 1 also, and which TOP 1 can be specified there (as shown).
Just for fun, this is another way to do it which performs very closely to the above query (-15ms to +30ms). While it's more complicated than necessary for such a simple query, it demonstrates a technique that I don't see other SQL folks using very often.
SELECT
ID = Coalesce(T.ID, 0),
Name = Coalesce(T.Name, '')
FROM
(SELECT 1) X (Num)
LEFT JOIN (
SELECT TOP 1 ID, Name
FROM TableName
ORDER BY ID DESC
) T ON 1 = 1 -- effective cross join but does not limit rows in the first table
Use:
SELECT TOP 1
x.id,
x.name
FROM (SELECT t.id,
t.name
FROM TABLENAME t
UNION ALL
SELECT 0,
'') x
ORDER BY id DESC
Using a CTE equivalent:
WITH query AS (
SELECT t.id,
t.name
FROM TABLENAME t
UNION ALL
SELECT 0,
'')
SELECT TOP 1
x.id,
x.name
FROM query x
ORDER BY x.id DESC
CREATE TABLE #sample(id INT, data VARCHAR(10))
SELECT TOP 1 id, data INTO #temp FROM #sample
IF ##ROWCOUNT = 0 INSERT INTO #temp VALUES (null, null)
SELECT * FROM #temp
put the top oustide of the UNION query
SELECT TOP 1 * FROM(
SELECT ID,Name
FROM TableName
UNION ALL
SELECT 0,''
) z
ORDER BY ID DESC
IF EXISTS ( SELECT TOP 1 ID, Name FROM TableName )
BEGIN
SELECT TOP 1 ID, Name FROM TableName
END
ELSE
BEGIN
--exists returned no rows
--send a default row
SELECT 0, ''
END