Pagination of large dataset - sql

I have a query that returns a large (10000+ rows) dataset. I want to order by date desc, and display the first 40 results. Is there a way to run a query like this that only retrieves those 40 results without retrieving all 10000 first?
I have something like this:
select rownum, date, * from table
order by date desc
This selects all the data and orders it by date, but the rownum is not in order so it is useless for selecting only the first 40.
ROW_NUMBER() over (ORDER BY date desc) AS rowNumber
^ Will display a rownumber in order, but I can't use it in a where clause because it is a window function. I could run this:
select * from (select ROW_NUMBER() over (ORDER BY date desc) AS rowNumber,
rownum, * from table
order by date desc) where rowNumber between pageStart and pageEnd
but this is selecting all 10000 rows. How can I do this efficiently?

SELECT *
FROM (SELECT *
FROM table
ORDER BY date DESC)
WHERE rownum <= 40
will return the first 40 rows ordered by date. If there is an index on date that can be used to find these rows, and assuming statistics are up to date, Oracle should choose to use that index to identify the 40 rows that you want and then do 40 single-row lookups against the table to retrieve the rest of the data. You could throw a /*+ first_rows(40) */ hint into the inner query if you want though that shouldn't have any effect.
For a more general discussion on pagination queries and Top N queries, here's a nice discussion from Tom Kyte and a much longer AskTom discussion.

Oracle 12c has introduced a row limiting clause:
SELECT *
FROM table
ORDER BY "date" DESC
FETCH FIRST 40 ROWS ONLY;
In earlier versions you can do:
SELECT *
FROM ( SELECT *
FROM table
ORDER BY "date" DESC )
WHERE ROWNUM <= 40;
or
SELECT *
FROM ( SELECT *,
ROW_NUMBER() OVER ( ORDER BY "date" DESC ) AS RN
FROM table )
WHERE RN <= 40;
or
SELECT *
FROM TEST
WHERE ROWID IN ( SELECT ROWID
FROM ( SELECT "Date" FROM TEST ORDER BY "Date" DESC )
WHERE ROWNUM <= 40 );
Whatever you do, the database will need to look through all the values in the date column to find the 40 first items.

You don't need a window function. See
http://www.techonthenet.com/oracle/questions/top_records.php
for an answer to your problem.

Related

Multiple records not repeated

I have a table called TABLE_SCREW where I want to get the latest records for each code.
For example, in the table below you should obtain the records with ids 3 and 7.
I am a newbie in sql and I hope you can help me.
You could use:
SELECT TOP 1 WITH TIES *
FROM TABLE_SCREW
ORDER BY ROW_NUMBER() OVER(PARTITION BY CODE ORDER BY Date DESC);
Another approach(may have better performance):
SELECT * -- here * should be replaced with actual column names
FROM (SELECT *,ROW_NUMBER() OVER(PARTITION BY CODE ORDER BY Date DESC) AS rn
FROM TABLE_SCREW) sub
WHERE sub.rn = 1;

How to select a row based on its row number?

I'm working on a small project in which I'll need to select a record from a temporary table based on the actual row number of the record.
How can I select a record based on its row number?
A couple of the other answers touched on the problem, but this might explain. There really isn't an order implied in SQL (set theory). So to refer to the "fifth row" requires you to introduce the concept
Select *
From
(
Select
Row_Number() Over (Order By SomeField) As RowNum
, *
From TheTable
) t2
Where RowNum = 5
In the subquery, a row number is "created" by defining the order you expect. Now the outer query is able to pull the fifth entry out of that ordered set.
Technically SQL Rows do not have "RowNumbers" in their tables. Some implementations (Oracle, I think) provide one of their own, but that's not standard and SQL Server/T-SQL does not. You can add one to the table (sort of) with an IDENTITY column.
Or you can add one (for real) in a query with the ROW_NUMBER() function, but unless you specify your own unique ORDER for the rows, the ROW_NUMBERS will be assigned non-deterministically.
What you're looking for is the row_number() function, as Kaf mentioned in the comments.
Here is an example:
WITH MyCte AS
(
SELECT employee_id,
RowNum = row_number() OVER ( order by employee_id )
FROM V_EMPLOYEE
ORDER BY Employee_ID
)
SELECT employee_id
FROM MyCte
WHERE RowNum > 0
There are 3 ways of doing this.
Suppose u have an employee table with the columns as emp_id, emp_name, salary. You need the top 10 employees who has highest salary.
Using row_number() analytic function
Select * from
( select emp_id,emp_name,row_number() over (order by salary desc) rank
from employee)
where rank<=10
Using rank() analytic function
Select * from
( select emp_id,emp_name,rank() over (order by salary desc) rank
from employee)
where rank<=10
Using rownum
select * from
(select * from employee order by salary desc)
where rownum<=10;
This will give you the rows of the table without being re-ordered by some set of values:
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT '1')) AS RowID, * FROM #table
If using SQL Server 2012 you can now use offset/fetch:
declare #rowIndexToFetch int
set #rowIndexToFetch = 0
select
*
from
dbo.EntityA ea
order by
ea.Id
offset #rowIndexToFetch rows
fetch next 1 rows only

Apply aggregate function on subset of rows

I have a table that I want to calculate the average of one column but only for the last 10 rows.
SELECT AVG(columnName) as avg FROM tableName
I cannot apply top directly since this query only returns one row. I need a way to get the latest 10 rows and do the average on them.
Try this:
SELECT AVG(columnName) FROM
(SELECT TOP 10 columnName FROM tableName ORDER BY ColumnWhichHoldsOrder DESC) A
select avg(columnName)
from (
select columnName,
row_number() over (order by some column desc) as rn
from tableName
) t
where rn <= 10;

SQL query query about selecting particular number of rows

For example -
SELECT * FROM user_names returns about 100 rows.
How would you query to get only row no.30 to row no.40?
Well with MySQL you would do it as follows:
SELECT * FROM user_names LIMIT 30,10
you can use row_number
with CTE_Table
as (SELECT id, ROW_NUMBER() OVER(ORDER BY id DESC) AS Row_Number
FROM dbo.a)
select * from CTE_Table
WHERE Row_Number BETWEEN 30 AND 40
Assuming SQL Server
SELECT * FROM
(SELECT ROW_NUMBER() OVER (ORDER BY user_names.id) as RowNumber, *
from user_names) un
where un.RowNumber between 30 and 40
replace the ORDER BY clause with whatever you want to order by.

Retrieving the second most highest value from a table

How do I retrieve the second highest value from a table?
select max(val) from table where val < (select max(val) form table)
In MySQL you could for instance use LIMIT 1, 1:
SELECT col FROM tbl ORDER BY col DESC LIMIT 1, 1
See the MySQL reference manual: SELECT Syntax).
The LIMIT clause can be used to constrain the number of rows returned by the SELECT statement. LIMIT takes one or two numeric arguments, which must both be nonnegative integer constants (except when using prepared statements).
With two arguments, the first argument specifies the offset of the first row to return, and the second specifies the maximum number of rows to return. The offset of the initial row is 0 (not 1):
SELECT * FROM tbl LIMIT 5,10; # Retrieve rows 6-15
select top 2 field_name from table_name order by field_name desc limit 1
SELECT E.lastname, E.salary FROM employees E
WHERE 2 = (SELECT COUNT(*) FROM employess E2
WHERE E2.salary > E.salary)
Taken from here
This works in almost all Dbs
Select Top 1 sq.ColumnToSelect
From
(Select Top 2 ColumnToSelect
From MyTable
Order by ColumnToSelect Desc
)sq
Order by sq.ColumnToSelect asc
Cool, this is almost like Code Golf.
Microsoft SQL Server 2005 and higher:
SELECT *
FROM (
SELECT
*,
row_number() OVER (ORDER BY var DESC) AS ranking
FROM table
) AS q
WHERE ranking = 2
Try this
SELECT * FROM
(SELECT empno, deptno, sal,
DENSE_RANK() OVER (PARTITION BY deptno ORDER BY sal DESC NULLS LAST) DENSE_RANK
FROM emp)
WHERE DENSE_RANK = 2;
This works in both Oracle and SQL Server.
Try this
SELECT TOP 1 Column FROM Table WHERE Column < (SELECT MAX(Column) FROM Table)
ORDER BY Column DESC
SELECT TOP 1 Column FROM (SELECT TOP <n> Column FROM Table ORDER BY Column DESC)
ORDER BY ASC
change the n to get the value of any position
Maybe:
SELECT * FROM table ORDER BY value DESC LIMIT 1, 1
one solution would be like this:
SELECT var FROM table ORDER BY var DESC LIMIT 1,1