Returning a certain number of results from the top - sql

How do I return a certain number of rows, that are a certain number of rows from the top of a SELECT query?
What I mean is, suppose I had a table with 1000 rows. Suppose I want the first 50 rows, then I want the second 50 rows, then the third 50 rows, and so on. I know that TOP or LIMIT will limit the number of rows that are returned, but I am unsure how to tell SQL to get me the rows from a certain point in the returned table.

In Sql server you can make use of ROW_NUMBER() function to do thing you want
SELECT ROW_NUMBER() OVER(ORDER BY col3 DESC) AS Row,
col1, col2, col3
FROM tablename
in short
WITH ctetable AS
(
SELECT ROW_NUMBER() OVER(ORDER BY col3 DESC) AS RowNumber ,
col1, col2, col3
FROM tablename
)
SELECT *
FROM ctetable
WHERE RowNumber BETWEEN 1 AND 50;

SELECT TOP 50 * FROM
(
SELECT ROW_NUMBER() OVER(ORDER BY ID DESC) AS [Index], * FROM MyTable
) AS A
WHERE A.[Index] BETWEEN #StartIndex AND #EndIndex

;With CTE as(SELECT ROW_NUMBER() OVER(ORDER BY (select 0)) AS rno, * FROM MyTable)
select * from CTE where rno between start and end;

Related

How to select first-n/top-n rows from a query's resultant if its count is more than a given number?

I have a query that returns more than 1000 rows.
Step1:
with total_res as (
select table1.col1, table1.col2, table2.col3,... table2.coln
from table1 join table2
on table1.keycol=table2.keycol
where table1.col4='ABCD' and table2.col5 <= '02-02-2022'
order by table1.col1 desc)
In my requirement, I have to return the first 350rows by ordering col3 in desc if the output of the above query contain more than 1000rows.
So I added a row number column like below to add sequential numbers to the resulset from above.
Step2:
select col1, col2, col2...coln, ROW_NUMBER() OVER (ORDER BY col3 desc) as number from total_res;
What I don't understand now is how can I check if the output from step2 contains more than 350 rows and if so, select the first 350 rows.
Could anyone let me know how can I achieve this ? Or is there a better way to do it than using row_number ?
try like below in 2nd step check highest number>350 and then limit the value 350
with cte as
(
select col1, col2, col2...coln, ROW_NUMBER() OVER (ORDER BY col3 as number from total_res
) select * from cte
where 350 < ( select max(number) from cte)
order by number
limit 350

Random records in Oracle table based on conditions

I have a Oracle table with the following columns
Table Structure
In a query I need to return all the records with CPER>=40 which is trivial. However, apart from CPER>=40 I need to list 5 random records for each CPID.
I have attached a sample list of records. However, in my table I have around 50,000 records.
Appreciate if you can help.
Oracle solution:
with CTE as
(
select t1.*,
row_number() over(order by DBMS_RANDOM.VALUE) as rn -- random order assigned
from MyTable t1
where CPID <40
)
select *
from CTE
where rn <=5 -- pick 5 at random
union all
select t2.*, null
from my_table t2
where CPID >= 40
SQL Server:
with CTE as
(
select t1.*,
row_number() over(order by newid()) as rn -- random order assigned
from MyTable t1
where CPID <40
)
select *
from CTE
where rn <=5 -- pick 5 at random
union all
select t2.*, null
from my_table t2
where CPID >= 40
How about something like this...
SELECT *
FROM (SELECT CID,
CVAL,
CPID,
CPER,
Row_number() OVER (partition BY CPID ORDER BY CPID ASC ) AS RN
FROM Table) tmp
WHERE CPER>=40 OR pids <= 5
However, this is not random.
Assuming that you want five additional random records, you can do:
select t.*
from (select t.*,
row_number() over (partition by cpid,
(case when cper >= 40 then 1 else 2 end)
order by dbms_random.value
) as seqnum
from t
) t
where seqnum <= 5 or cper >= 40;
The row_number() is enumerating the rows for each cpid in two groups -- based on the cper value. The outer where is taking all cper values in the range you want as well as five from the other group.

Applying LIMIT and OFFSET to MS SQL server 2008 queries

I need to apply LIMIT and OFFSET to original query (without modifying it) in MSSQL server 2008.
Let's say the original query is:
SELECT * FROM energy_usage
(But it can be any arbitrary SELECT query)
That's what I came up with so far:
1. It does what I need, but the query generates extra column row_number which I don't need.
WITH OrderedTable AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, * FROM energy_usage
)
SELECT * FROM OrderedTable WHERE row_number BETWEEN 1 AND 10
2. This one doesn't work for some reason and returns the following error.
SELECT real_sql.* FROM (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, * FROM (SELECT * FROM energy_usage) as real_sql) as subquery
WHERE row_number BETWEEN 1 AND 10
More common case is:
SELECT real_sql.* FROM (
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, * FROM (real sql query) as real_sql) as subquery
WHERE row_number BETWEEN {offset} + 1 AND {limit} + {offset}
Error:
The column prefix 'real_sql' does not match with a table name or alias
name used in the query.
Simply do not put it on SELECT list:
WITH OrderedTable AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, *
FROM energy_usage
)
SELECT col1, col2, col3 FROM OrderedTable WHERE row_number BETWEEN 1 AND 10;
SELECT * is common anti-pattern and should be avoided anyway. Plus ORDER BY (SELECT 1) will not give you guarantee of stable sort between executions.
Second if you need only ten rows use:
SELECT TOP 10 *
FROM energy_usage
ORDER BY ...
Unfortunately you won't get something nice as Selecting all Columns Except One
WITH OrderedTable AS
(
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS row_number, *
FROM energy_usage
)
SELECT * EXCEPT row_number FROM OrderedTable WHERE row_number BETWEEN 1 AND 10;
This would solve the problem.
DECLARE #offset INT = 1;
DECLARE #limit INT = 10;
WITH Filtered AS (
SELECT TOP (#offset + #limit) *
FROM energy_usage
ORDER BY 1 ASC
), Results AS (
SELECT TOP (#limit) *
FROM Filtered
ORDER BY 1 DESC
)
SELECT *
FROM Results
ORDER BY 1 ASC;

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

Select top and bottom rows

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